[comp.lang.c++] different member functions with same arg types?

montnaro@spyder.crd.ge.com (Skip Montanaro) (01/19/91)

I am writing a parser class for a small language. I'd like it to be able to
parse input from strings and files. The natural class interface (for me) is
something like:

    class parser : public ...
    {
	...
	public:
	    parse(char *string);
	    parse(char *filename);
	...
    }

Unfortunately, both strings and filenames are char *'s, so the compiler
can't distinguish the two interfaces. Is it kosher to add a dummy argument
to one member function? It seems crude to me:

    class parser : public ...
    {
	...
	public:
	    parse(char *string);
	    parse(char *filename, unsigned int dummy);
	...
    }

The other idea that occurs to me is to use a class to contain either the
string or the filename, but not both:

    class parser : public ...
    {
	...
	public:
	    parse(String string);
	    parse(char *filename);
	...
    }

This seems like overkill, when all I want is the string's value, not all the
other baggage of the String class.

I'm open to suggestions for alternate interfaces.

(BTW, I'm a C++ novice.)

Thx,

--
Skip (montanaro@crdgw1.ge.com)

rmartin@clear.com (Bob Martin) (01/22/91)

In article <MONTNARO.91Jan18230744@spyder.crd.ge.com> montanaro@crdgw1.ge.com (Skip Montanaro) writes:
>
>I am writing a parser class for a small language. I'd like it to be able to
>parse input from strings and files. The natural class interface (for me) is
>something like:
>
>    class parser : public ...
>    {
>	...
>	public:
>	    parse(char *string);
>	    parse(char *filename);
>	...
>    }
>
>Unfortunately, both strings and filenames are char *'s, so the compiler
>can't distinguish the two interfaces. Is it kosher to add a dummy argument
>to one member function? It seems crude to me:
>
>    class parser : public ...
>    {
>	...
>	public:
>	    parse(char *string);
>	    parse(char *filename, unsigned int dummy);
>	...
>    }

   I agree that this is crude.  It is also dangerous since you, or another
   programmer may forget to use the dummy and accidentaly induce a bug which
   could be very hard to find.
>
>The other idea that occurs to me is to use a class to contain either the
>string or the filename, but not both:
>This seems like overkill, when all I want is the string's value, not all the
>other baggage of the String class.
>

On the contrary, I think this is an excellent solution.  A class to hold
strings and another class to hold file names is an obvious and compelling
separation of abstractions.  You could think of many things that you would
like to do to filename that you would not like to do to strings:  delete,
open, close, etc.

IMHO you should create these two separate classes.  Then the interface to
your parser will be very clear and unambiguous.
-- 
+-Robert C. Martin-----+:RRR:::CCC:M:::::M:| Nobody is responsible for |
| rmartin@clear.com    |:R::R:C::::M:M:M:M:| my words but me.  I want  |
| uunet!clrcom!rmartin |:RRR::C::::M::M::M:| all the credit, and all   |
+----------------------+:R::R::CCC:M:::::M:| the blame.  So there.     |

kaiser@ananke.stgt.sub.org (Andreas Kaiser) (01/22/91)

In a message of <Jan 19 04:07>, Skip Montanaro (montnaro@spyder.crd.ge.com ) 
writes: 

 SM>     class parser : public ...
 SM>     {
 SM>         ...
 SM>         public:
 SM>             parse(char *string);
 SM>             parse(char *filename);
 SM>         ...
 SM>     }

 SM> Unfortunately, both strings and filenames are char *'s, so the compiler
 SM> can't distinguish the two interfaces. Is it kosher to add a dummy argument
 SM> to one member function? It seems crude to me:

How about
               parse_string(char *);
               parse_file(char *);
?

Dummy members are of course possible although ugly, but I don't see any reason, 
why you don't whant to use different names for this purpose. It makes the code 
better readable. It is not only the compiler which might get into trouble, 
deciding which function you really want to call.

                Andreas 
 

jamshid@ut-emx.uucp (Jamshid Afshar) (01/22/91)

In article <MONTNARO.91Jan18230744@spyder.crd.ge.com> montanaro@crdgw1.ge.com (Skip Montanaro) writes:
>
>I am writing a parser class for a small language. I'd like it to be able to
>parse input from strings and files. The natural class interface (for me) is
>something like:
>    class parser : public ...
>    { ...
>	public:
>	    parse(char *string);
>	    parse(char *filename);
>	...
>    }
<text about how the compiler cannot distinguish these two, and some>
<possible fixes deleted>
>
>I'm open to suggestions for alternate interfaces.
>
>Skip (montanaro@crdgw1.ge.com)

This may be overkill for what you want, but why don't you make an
abstract class, say SourceCode, that has pure virtual functions
for everything that a parser will want to do with some source code.

class SourceCode {
public:
   virtual void init() = 0;
   virtual char getChar() = 0;
   virtual void putBackChar(char) = 0;
   virtual void skipWhiteSpace() = 0;
   virtual bool isAtEnd() = 0;
   virtual void close() = 0;
   ...
};

Then derive new classes, 'SourceFromFile' and 'SourceFromString', from
SourceCode and implement those virtual functions however they need to
be implemented.  SourceFromFile, for example, would have an ifstream
object that is opened in init().  SourceFromString would have in
addition to a 'const char *' (or better yet, a String), an index to
indicate the current position in the string.

Now, have the Parser constructor take a reference or a pointer to a
SourceCode object so you can give Parser anything that can be derived
from SourceCode.  You might even be able to make SourceCode more
intelligent, like a 'Token getToken()' function, or skipUntil(const char
*) which could be implemented based on getChar().

The hard part, of course, is figuring out what functionality Parser
needs from a SourceCode object.  You also have to decide in which
class to put some of the functionality you need.  For example, does
the parser build a token from getChar(), or is a source_code
intelligent enough to break itself up into tokens?  I don't think
there are any "right" answers in situations like this-- it's all a
matter of design.  I would appreciate any comments on this approach.
BTW, aren't there C++ versions of yacc or troff or something that
would need to parse a file?  What approach(es) do they use?  It seems
like the problem of compiling source code is an excellent candidate
for an OO approach.

Jamshid Afshar
jamshid@emx.utexas.edu

davel@cai.uucp (David W. Lauderback) (01/24/91)

In article <42908@ut-emx.uucp> jamshid@ut-emx.uucp (Jamshid Afshar) writes:
>In article <MONTNARO.91Jan18230744@spyder.crd.ge.com> montanaro@crdgw1.ge.com (Skip Montanaro) writes:
>>
>>I am writing a parser class for a small language. I'd like it to be able to
>>parse input from strings and files. The natural class interface (for me) is
>>something like:
>>    class parser : public ...
>>    { ...
>>	public:
>>	    parse(char *string);
>>	    parse(char *filename);
>>	...
>>    }
><text about how the compiler cannot distinguish these two, and some>
><possible fixes deleted>
>>
>>I'm open to suggestions for alternate interfaces.
>>
>>Skip (montanaro@crdgw1.ge.com)
>
>This may be overkill for what you want, but why don't you make an
>abstract class, say SourceCode, that has pure virtual functions
>for everything that a parser will want to do with some source code.
>
>class SourceCode {
>public:
>   virtual void init() = 0;
>   virtual char getChar() = 0;
>   virtual void putBackChar(char) = 0;
>   virtual void skipWhiteSpace() = 0;
>   virtual bool isAtEnd() = 0;
>   virtual void close() = 0;
>   ...
>};
>
>Then derive new classes, 'SourceFromFile' and 'SourceFromString', from
>SourceCode and implement those virtual functions however they need to
>  more suff...
>
>Jamshid Afshar
>jamshid@emx.utexas.edu

I have not use C++ very much and do not have easy access to a C++
compiler to test this out but could Skip use something like the following
code to solve his problem?

typedef	char *	FileName;

class parser : public ...
{	...
    public:
	parse(FileName filename);
	parse(char *string);
	...
}

char *test_string = "Test string";
FileName  test_file = (FileName) "TestFile";

func()
{
	class	parser	x;
	...
	x.parse("Test string");
	x.parse((FileName) "TestFile");
	x.parse(test_string);
	x.parse(test_file);
}

Or would C++ not know the difference between FileName and char *, since
there both really the same thing.  In any case, FileName could be made a
class, instead of just a typedef, to resolve the function selection problem.

Am I right here or way out in left-field?
-- 
David W. Lauderback (a.k.a. uunet!cai!davel)
Century Analysis Incorporated
Disclaimer: Any relationship between my opinions and my employer's
	    opinions is purely accidental.