Link to home
Start Free TrialLog in
Avatar of Axter
AxterFlag for United States of America

asked on

std::string wrapper class

I'm trying to create a class that emulates CString using std::string.
I'm having problems getting it to work withh printf and associated functions that use va_list.
If I have a class who's parent class is CString the following code will work.

CString_Child mystr = "hello";
printf("%s",mystr);

How ever if the parent class is std::string it fails.  When the program runs, it spit's out junk (non ASCII) in Unix, and it crashes in VC++.

So my question is does any one know what overloading function(s) I have to add/modify to the child class to get it to function like CString in printf?
Avatar of Axter
Axter
Flag of United States of America image

ASKER

Please post answer’s as comments, and I will promptly convert the first exceptable comment to an answer.

FYI:
I’m only looking for a method that will allow me to keep std::string as the parent class.  Any other method will not be accepted as an answer.
I also know that the above code could work if I just added the .c_str() syntax to it, but I’m looking only for a solution that will work with the posted syntax.

Thanks in advance!!!
Avatar of nietod
nietod

You just need to add a char * conversion operator.  Like

String : public string
{
public:
   operator const char *() const
   {
        return c_str();
   }
};

Actually, that probably won't work.

You will probably have to do

printf("%s"(const char *),mystr);

That is the best you can do for this
You might consider.

String : public string
{
public:
   const char *operator & () const
   {
      return c_str();
   }
};

There are some implications to that however.
Avatar of Axter

ASKER

>>operator const char *() const

That's the first thing I tried, but it failed.

>>printf("%s"(const char *),mystr);
As I stated in my comment, I'm not interested in changing the syntax.

>>const char *operator & () const

That also fails.  Same failure as posted in question.
Avatar of Axter

ASKER

There has to be some property that std::string is changing to the class that makes it fail when it becomes the parent class.  But I can't seem to put my finger on it.
Either of those works.

use oprator const char *, but then you have to force the conversion to const char *.  

Or overload operator &.  But then you have to speciifyt the address operator before the parameter and you can't generate pointers to the class.


Other than rewritting the strign class that is yoru only choice.

The CStrign class is a binary equivalent to a "char *".   So it can be used in place of a "char *".   This is obviously not true of the STL class--form many reasons.
>> As I stated in my comment, I'm not
>> interested in changing the syntax.
But do you want a solution?  There can be no solution without changing the syntax or rewrittin the STL stirng class form scratch.  

(And I am not saying that I don't know of a solution.  I am sayin that there is no solution.  There is a big difference.)

>> There has to be some property that std::string is
>> changing to the class that makes it fail when it
>> becomes the parent class.  But I can't seem to
>>  put my finger on it.
You have it backwards.  There is nothing about STL string that makes if fail.  Its just that there is 1 "peculariaty" about the CString class that allows it to succeed.

when you call printf() the object is copied onto the call stack.  When a parameter is found to correspond to "%s", then the parameter has to have the exact binary format of the a "char *"  That means it must point to a NUL terminated string, it must be (in win32) 32 bits in size.   It just so happens (well it was no accident) that this is all true of MFC's CString.  But none of this is true of the STL string.  The STL string is not NUL terminated.  The STL string class may have data members that make it larger than 32 bits and it is possible (probable) that none of  those data are pointers to a string.  

See?  

You can;'t change the way that the STL string is represented.  You can't change the way that the compiler places it on the stack.  You can't change what printf() expects on the stack..   So there is no solution, unless you relax your restrictions.



Avatar of Axter

ASKER

>>But do you want a solution?
A solution only with the previously described limitations.  The class has to be able to emulate Cstring, or it’s of no use to me.
Also I know I could get the class to work if I make std::string a sub variable instead of the parent class, but then I have to completely rewrite the current class, which I’m trying to avoid.

If what you posted is the problem, then I could still make it work by using a mutable variable in the wrapper class.
However, I don't think that's the problem, because I put break points in the operators, and the code does not jump into the operators.
If I could get it to jump into the operator, I know I could manipulate the wrapper class to work.  I just can’t seem to get it to go there.
Avatar of Axter

ASKER

I'll raise this to 250 pts total for the first person that can give me a working solution.
Avatar of Axter

ASKER

nietod
>>There can be no solution without changing the syntax or rewrittin the STL stirng class form scratch.

If this was the case, all I should have to do is change the wrapper class.  
Exactly what change I need to make to the wrapper class, is the basic question.
>> A solution only with the previously described limitations.  
>> The class has to be able to emulate Cstring, or it’s
>> of no use to me.
but that is impossible.  

>> then I could still make it work by using a mutable
>> variable in the wrapper class.
Nope.  One most compilers and certainly on Vc and BC and BCB the layour of a derived class will always begin with the layout of the base class.  So no data member you add to the class will work, because it comes too late.   Besides in any case you class will always be the wrong size, So any parameters following it wil be inaccessible.

>> tf this was the case, all I should have to do is change the wrapper class.  
But you class cannot derive from STL string, nor can it contain an STL string as a data member.  The class must contain exactly 1 pointer, not other non-static data member and not base classes with any non-stati data members.  This pointer must point to a NUL terminated string.

Do you see that?

You really woulf be best off writting your own string class.  That is the only solution with hope.

Or look at it another way.  The singlemost common cause of problems in C was the use of char * strings.  String mistakes account for somrthing like 10% of the errors in production code.  (I don't remember the exact figure.)  One of the next items that causes errors, I don't remember if it is next, or 3rd or 4th, but its almost certainly in the top 10, is the use of variable parameters.   C++ provides mechanims that allow us to avoid theses.  In fact avoiding these problems is one reason why C++ exists and why it has the features that it does.

Perhaps you should reconsider using sprintf() entirely.  Using stream object will lead to a far more powerful solution that is far more error free and is likely to require no effort on your part.

Definitely worth considering.
Avatar of Axter

ASKER

nietod,
I guess I"m going to have to explain why I need it with the previously described limitations.
I"m createing a set of classes that emulates MFC classes on a UNIX platform.

In order to port MFC code as is, the emulated CString class has to work exactly as it works in MFC.
This problem doesn't just occure with printf, it aslo occurs on other functions that use va_list.

I know you think this is not possible, but I want to keep the question opened for a posible answer.
If I don't get an answer, I'll delete the question.

I beleive the solution is in overloading an operator, but it doesn't seem as though any operators are used in a fuction useing a va_list using the following format:
printf("test %s", class_name);

It only goes through an operator when using &.  As in
printf("test %s", &class_name);


It was nice class on www.codeguru.com, which does similar things
Axter:

nietod is correct. Overloading an operator won't help you in this case. There are plain rules for variable argument lists: certain expansions occur (like float->double), and everything that's "unknown" to C will be passed by value - i.e. a copy will be placed on stack.
CString is exactly designed the way it is, to WORK with printf-like things. It's a bit on the dirty side, but it works.

Guess, for your problem it would be easier NOT to use std::string as implementation base.

Rip off the CString sources from MFC. terrible job, but works..

Have a good time
Peter
>> I guess I"m going to have to explain why I need it with the
>> previously described limitations.  I"m createing a set of
>> classes that emulates MFC classes on a UNIX platform.
Then don't use an STL string at all.  Write it from sratch--or basically just steel the CString source and tweek it.  You will have to do that anyways since CString and STL strign have different behaviors anyways.   It would be nearly hopeless certainl;y increadibly difficult to get all the CString behaviors working using an STL string sinc they are beased on such diffferent philosphies.

>> I know you think this is not possible,
No.  That is not what I said.

I said that I KNOW it is impossible.  There is a diference.  I was able to explan exactly why it was impossible.  Did you understand my explanation?

>> I beleive the solution is in overloading an operator,
Unless the caller explicitly uses an operator, no operator will be used.  Those were the two solutions I provided, but you didn't want to explicitly provide the oeprators.   If you don't use an operator, the data will be passed by the copy constructor.  In that case you class has to have the exact same format as a character pointer.  (with no additional members)  Obviously that is impossible if you use STL string in any way.

>> it doesn't seem as though any operators are used in a
>> fuction useing a va_list using the following format:
Right.  That is my point.  
Avatar of Axter

ASKER

>>Did you understand my explanation?
No.

I'm still going to hold out for a possible answer.
If no acceptable answer is given within the next 5 days, I'll delete the question.
When printf() encounters a %s in the format string there must be a "char *" pointer on the stack in the cooresponding position.  This is because that is the only data type that printf() was written to handle for %s.  Does that make sense?

The data on the stack corresponding to that %s must also be the exact size as a "char *"  This is because printf will assume that the data is only a "char *" and will use sizeof(char *) to locate the next parameter on the stack.  Does that make sense?

The only possible conclusion from this is that the data passed must actually BE a "char *" otherwise printf() will not be able to handle it correctly.  makes sense?  Agreed?

So the question becomes "How to pass a "char *" in this case".  Agreed?

Well if you use an object in a varaible parameter call it is passed using its copy constructor.   So that means that a copy of your string class will be passed.  i.e. an object of your string classs will be passed.  You have no control over this.  This is a fundamental rule.  Makes sense?

So now we have that printf() requires that that a char * be passed and that you will pass an object of yoru class.   The only way to make these two reconcile is that your class actually be binary compatible with a char *.   The CString class is.  The STL class is not.   Does that makes sense?

You might say,well what if I derive from STL string and store a char * pointer as a data member.  Nope.  The class will be too large and it the binary layout will begin with the STL string, not the char *   printf() won't ever see the "char *".

What if I store a char * data mebmer followed by an STL string data member.  Now that works, in part.  printf() will find the char * part and will pritn with it correctly.  But then it chokes when it tries to find the next parameter because your data was larger than it expected.

Does that make sense?

if you really want CString behavior, why not just use CString.   I don't like the CStrign design overall, but it is does do this well.  it is simple, you shoudl have no problem converting it for any platform.

CString's only non-static data members is a poointer to the character string.  That is why it can be passed in place of a char *  it is the binary equivalent of a char.   (non-portable assumption, but likely to be the case.)   The string however is reference counted.  Located before the first character of the string there is an int that has the reference count.  you can just sizeof(int) to backup the pointer and get access to t he reference count.  

Actually I think MFC stores a whole object before the string, not just the reference count.  it probably stores the length of the allcoated array too, so it knows how much room is left for expansion.

Take a look at AFX.h (I think).
Avatar of Axter

ASKER

>>if you really want CString behavior, why not just use CString.   I don't like the CStrign design overall, but it is does do this well.  it is simple, you shoudl have no problem converting it for any platform.

I already made a full class that emulates CString.  The only thing it doesn't do is handle functions use va_list.
The CString class I  created has a std::string base class.

I already tried to use the MFC code for CString, but there were to many problems using it in Unix.

If I use any other method, it will mean I basically have to trash the current class I created, and I don't want to do that.

That's why I'm looking for a solution that will work with the specified criteria for an answer.
Anything else will not do me any good.
I already know I can do it by other means out side of the specified criteria, so an answer out side of the specified criteria will not be giving me anything that I didn't already know.

nietod, your last explanation was much clear, and I understand what you're saying, but I still want to see if someone has an answer within the criteria.

I'm not loosing anything by waiting for an answer.
>> i already tried to use the MFC code for CString, but
>> there were to many problems using it in Unix.
What problems.

>> If I use any other method, it will mean I basically have to
>> trash the current class I created,
That is your only choice. you either have to accept that it won't wotk with variable arguments or you have to rewrite it.  

Note however a rewrite is not really a guarantee, it will depend on how the compiler lays out the class, but you will usually be safe.

>> but I still want to see if
>> someone has an answer within the criteria.
What???

You just said you understood.    If you understood, then you _know_ that there is no possible solution.  That was a proof of why there is no possible solution.  it is not an explanation of why I _think_ there is no possible solution.  It is proof of why there is no possible solution.  read it again.  Do you see any room for a solution?  are any of those points unclear or in dispute?
Avatar of Axter

ASKER

>>Do you see any room for a solution?

I didn't see any room for a posible solution in the first place.  If I did, I wouldn't have posted the question.

What I'm looking for is solution that I (or you) may have over looked (with the stated criteria).
You are missing the point.  I prooved that there can be NO solution.   Read it again.  Its not that you didn't see a solution.  It not that I didn't see a solution.  Its that there can never be a solution.  Doesn't that seem significant?

I further prooved that the basis for your class--deriving from STL stirng is doomed to failure.  It makes your class the wrong size.

Your best bet is to rewrite from scratch.  You should be able to write this class in an hour or two.
Avatar of Axter

ASKER

Well Mr. Skeptic, I did find a solution.  Where there’s a well there’s a way.
The solution was easy.
Create a dummy class with a pointer as the first data member.  The target class gets derived from both the dummy class and the std::string class (with the dummy class listed first).
In my constructors, I assigned the dummy->ptr variable to std::string c_str() .
My next post will show the test class that I used.

Avatar of Axter

ASKER

class chr_buf
{
      void initFunc(void){memset(buffer,'\0',sizeof(buffer));ptr=buffer;};
public:
      const char *ptr;
      chr_buf(){initFunc();};
      chr_buf(const chr_buf& src){initFunc();ptr = src.ptr;};
      ~chr_buf(){;};
private:
      char buffer[6];
};

class str_xyz : protected chr_buf, private std::string
{
public:
      str_xyz(const str_xyz& stringSrc){assign(stringSrc.c_str());ptr=c_str();};
      str_xyz(){;};
      str_xyz(const char * text)
      {
            assign(text);
            ptr=c_str();
      };
      ~str_xyz(){;};
};

void someFunction(void)
{
      char test2[1333];
      str_xyz str_xyz__ = "Hey this really works";//;
      sprintf(test2,"str_xyz__ = %s\n",str_xyz__);
}
Avatar of Axter

ASKER

Thanks to all for trying.  I will be deleteing this question at the end of the day.
Sorry, that doesn't work.

Reread what I said.

Try

sprintf(test2,"str_xyz__ = %s %i\n",str_xyz__,10);

it doesn't print the int.  Why?

Avatar of Axter

ASKER

Sorry, but I just realized I didn't post any comments on the other experts post.

proskig,
Because I already have a class created to emulate CString, I don't want to use another class.  I have tested my class, and I know it's very ansi compatible.
Thanks anyway.

peterchen,
As I stated to nietod, I tried using the MFC CString class, but ran into to many problems.
Most of the problems where related to the use of non-Ansi C++ functions used through out the CString code.
Avatar of Axter

ASKER

>>sprintf(test2,"str_xyz__ = %s %i\n",str_xyz__,10);

You're right, but I think I can figure out away around that.

By the way, you mention previously something about the copy constructor being used in a printf function.
It doesn't use the copy constructor.  I tested it out.
>> It doesn't use the copy constructor.  I tested it out.
Yes, it does.  It has to.  

>> I think I can figure out away around that.
There are two possibilities.  You can't.  Or you can, but there is a problem with it.  I can say that with 100% confidence.

I'm not being skeptical.  I'm being logical.  If you take some time to think about it you will see that.  Unfortunately, I think you tend to dismiss everything I say.  That is unfortunate.  That will cost you.  You don't want to waste the time spent.  But you are wasting more and more time..
I should point out that your last solution failed because one of the restrictions I informed you of.  i.e. I had predicted that it would fail long before you proposed it.  Does that seem significant to you?
Avatar of Axter

ASKER

>>Yes, it does.  It has to.  
I tested this, and va_list functions don't use it.
I made a class, and put a break point in the copy constructor, and it did not travel through the copy constructor.

I don't dismiss everything you say.  Believe it or not, I do value your opinion.  But when what you say is disproved by test, how can you expect me to rely on it.

Wouldn't you do the same???
Avatar of Axter

ASKER

>>Your best bet is to rewrite from scratch.  You should be able to write this class in an hour or two.

If you think you can write this class in an hour or two, then you haven't really studied the CString class.

For one thing, the class will only be able to hold one data member.  If it has more then one data memeber, I will have the same problem I'm having with the current class.
The class also needs to work with both Wide and single char.
You have to make all the operator functions, and friend functions.
No way is this a two hour job.
>> you haven't really studied the CString class.
I studied it xtensively for my strign optimization article.  I didn't even mention it in the article because I felt it didn't measure up in soem respects.  But it is small-that is its claim to fame.

>> the class will only be able to hold one data member.  If it has
>> more then one data memeber, I will have the same problem
>> I'm having with the current class.
You're telling me this?  I think that has been my point.

>> No way is this a two hour job.
Converting the CString class should be a few hour job.  What sort of problems are you having?

>> I made a class, and put a break point in the copy constructor,
>> and it did not travel through the copy constructor.
That surprised me.  So I did a little exploring.  

If it was impossible before.  It is 1000 times more impossible that I would have guessed.

There is no standard as to how class based objects are passed to variable argument functions.  From   5.2.2.7

7 When there is no parameter for  a  given  argument,  the  argument  is
  passed  in such a way that the receiving function can obtain the value
  of the  argument  by  invoking  va_arg  (_lib.support.runtime_).   The
  lvalue-to-rvalue  (_conv.lval_),  array-to-pointer (_conv.array_), and
  function-to-pointer (_conv.func_) standard conversions  are  performed
  on  the argument expression.  After these conversions, if the argument
  does not have arithmetic, enumeration, pointer, pointer to member,  or
  class  type, the program is ill-formed.  If the argument has a non-POD
  class type (_class_), the behavior is undefined.  If the argument  has
  integral  or  enumeration  type that is subject to the integral promo-
  tions (_conv.prom_), or a floating point type that is subject  to  the
  floating point promotion (_conv.fpprom_), the value of the argument is
  converted to the promoted type before the call.  These promotions  are
  referred to as the default argument promotions.

The key there is what happens when you have a non-POD clas type.  Which you do.  The behavior is undefined.  It may call the copy constructor, it may perform a binary copy (death to a string object) it may just do so something illogical.

So in addition to all the points above about hwo the CStrign class works, you have to realize that it works becuase the programmers that wrote it also wrote the compiler....   Another compiler might not be as friendly.  

Given this fact I would NOT recommend trying to port CString.  Not for this one feature at least, because this feature depends on the compiler as well as the source.
Avatar of Axter

ASKER

>>Given this fact I would NOT recommend trying to port CString.  Not for this one feature at least, because this feature depends on the compiler as well as the source.

I've already tried to convince my project managers to give up the idea of trying to port MFC projects to Unix, but it fell on death ears.
I'm a contractor who gets paid the same to do it the right way or the wrong way.

Anyway, the bottom line, is that I'm going to have to get it to work one way or another.
At the end, I might have to tell them to use str.operator LPCTSTR() on all there functions that use va_list.
That way the class will work the same on both MFC (windows) and the simulated MFC on unix.

I rather not do that, so I'll leave this question open in the hopes that some one knows of a solution.
>> 'm a contractor who gets paid the same to do
>> it the right way or the wrong way.
I hope you get paid if you don't do it all.

>> At the end, I might have to tell them to
>> use str.operator LPCTSTR()
That would be the best of the portable options.
Avatar of Axter

ASKER

I know have gone through the trouble of recreating my CString class to a version that does not use std::string.
I Created the class very similar to the MFC CString class, in that it only has one member variable.

This class, which I renamed to CStringX, works just find with the VC++ compiler. But when I compile it under the g++ compiler, it still fails to work with va_list functions.
Furthermore, it also fails with the "?" operator.
Example:
CStringX test = "test";
char test1[32] = ((1==1)?test.operator LPCTSTR():"bla"); //This line works and compiles with out errors
char test2[32] = ((1==1)?test:"bla"); //This line fails and compiles with an error or warning

Much to my surprise, it doesn't look like you over write the "?" operator.

I'm frustrated.  I've created this class twice, and still can not get it to emulate the CString class on a Unix platform.

I've also looked at the CString class(es) listed in codeguru, and none of them can even come close to solving my problem.  I don't think  any of those authors even considered the va_list function problem.

So I'm now removing some of the limitations I posted on my question.  I'm looking for any suggestions that would help me create a CString class in Unix that would work with va_list functions and the "?" operator.

I don't care if I have to rewrite the CString class a third time.  I just want it to work exactly like CString.


a) the ? operator  
  What type cast operators does your class provide?
  (there's no need nor way to overload '?')


b) VarArgs work a bit different on UNIX, the following is from MSDN:

>>>>>>>>><
/* VA.C: The program below illustrates passing a variable
 * number of arguments using the following macros:
 *      va_start            va_arg              va_end
 *      va_list             va_dcl (UNIX only)
 */

#include <stdio.h>
#define ANSI            /* Comment out for UNIX version     */
#ifdef ANSI             /* ANSI compatible version          */
#include <stdarg.h>
int average( int first, ... );
#else                   /* UNIX compatible version          */
#include <varargs.h>
int average( va_list );
#endif

void main( void )
{
   /* Call with 3 integers (-1 is used as terminator). */
   printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );

   /* Call with 4 integers. */
   printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );

   /* Call with just -1 terminator. */
   printf( "Average is: %d\n", average( -1 ) );
}

/* Returns the average of a variable list of integers. */
#ifdef ANSI             /* ANSI compatible version    */
int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}
#else       /* UNIX compatible version must use old-style definition.  */
int average( va_alist )
va_dcl
{
   int i, count, sum;
   va_list marker;

   va_start( marker );            /* Initialize variable arguments. */
   for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )
      sum += i;
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}
#endif

Peter
>> it still fails to work with va_list functions.
What a surprise.  Here I've been saying again and again that there is no guarantee that it will ever work.  and it fails?  Gosh, who would expect that.  

>> char test2[32] = ((1==1)?test:"bla"); //This line fails and compiles with an error or warning
That line is incorrect.  the last two operands to the ?: operator  must be of the same type.  The compiler will not convert them to the same type (except CV conversions--I think.)

>> none of them can even come close to  solving my problem.  I
>> don't think  any of those authors even considered the va_list
>> function problem.
Of course not.  It is a stupid thing to do.  

I already told you that there is no provision int eh C++ standard for passing objects of classes to a VA function.  It works in MFC because the MFC programmers also wrote the VC compiler.   Unless you plan to write your own compiler you are playing with fire.   You can't really expect other programmers do this!

>>  I'm looking for any suggestions that would help me
>> create a CString class in Unix that would work with va_list
>> functions and the "?"  operator.
You can use the an operator const char *() function, Like I posted above.   but you need to realize that the operator is not performed autimatically int he two cases mentioned.  It doesn't occur automatically when you use operatpr ? : or when you call a VA function.   you will have to apply the oeprator manually.

>> I don't care if I have to rewrite the CString class a third time.
Your 1st version should be fine for this.  Howeve,r you need to realize that the STL string class has different properties than the MFC string class, so in some cases (like dealing with NUL characters) you could get different results.
Avatar of Axter

ASKER

nietod
>>That line is incorrect.  the last two operands to the ?: operator  must be of the same type.  The compiler
will not convert them to the same type (except CV conversions--I think.)

But this compiles with no problem in VC++.
Example:
CString test1 = "hello";
char test2[]="goodby";
CString lasttest = ((1==1)?test1:test2);

The above code will compile in VC++.  If it's not supposed to, than it must be a VC compiler issue.

>>You can use the an operator const char *()

I already had that as my backup plan.

>>I already told you that there is no provision int eh C++
Well it's looks like you're right, but I'm still going to try and see if someone has something up their sleeve that I haven't thought of.
Besides, I really hate to give up.


Avatar of Axter

ASKER

peterchen,
>>nor way to overload '?'

That's what I try stating in my comment, but I left a word out.  It should have read "Much to my surprise, it doesn't look like you 'CAN' over write the "?" operator"

>>there's no need
I do have a need for it, but since it can't be done, it's kind of a mute point.

>>b) VarArgs work a bit different on UNIX, the following is from MSDN:
I'm aware of the difference between Unix and PC va_list.
The defferences is not my problem.  
Please read previous comments for more info.
>> But this compiles with no problem in VC++.
This was a very late change.  VC might not support it yet.   But I'm 90% sure it is correct.

I believe only CV conversions are applied automatically.   I think there are also some provissions about void * in it.
The draft standard seems to say that conversions are applied.   But I think that is incorrect.  It definitely raises some issues that they do not answer.   Like there may be more than one conversion that satisfies the expression, depending on whthor or not you try to convert the 2nd or 3rd operand, or both.  That sort of ambiguiity is a problem.

In any case is you conversion working in the obvious cases?  like can you call a function that takes a parameter of type "const char *".  
Avatar of Axter

ASKER

>>In any case is you conversion working in the obvious cases?  

With the Unix compiler it only works if I use .operator LPCTSTR().

If I don't use the operator, I get either a compiler warning, or a compiler error.
>> With the Unix compiler it only works if I use .operator LPCTSTR().
You are going to have to in order to be portable.

Does that mean all the problems are solved?  At least as much as standard C++ allows?
Avatar of Axter

ASKER

I didn't get anything solved.  My class is working no better or no worse then when I first posted this question.

Until I can figure out a better solution, I'm using the operator LPCTSTR()

I've also added some extra Format functions to CString.
void Format(const TCHAR * lpszFormat, CString &src);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2, CString &src3);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2, CString &src3, CString &src4);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2, CString &src3, CString &src4, CString &src5);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2, CString &src3, CString &src4, CString &src5, CString &src6);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2, CString &src3, CString &src4, CString &src5, CString &src6, CString &src7);
void Format(const TCHAR * lpszFormat, CString &src1, CString &src2, CString &src3, CString &src4, CString &src5, CString &src6, CString &src7, CString &src8);
void Format(const TCHAR * lpszFormat, CString &src, int i_src);
void Format(const TCHAR * lpszFormat, int i_src, CString &src);

These functions helps in limiting the amount of code that has to get modified.
>> I've also added some extra Format functions to CString
That seems like a dead end.  You don't have much flexibility with that approach and it is costly in terms of space.

I would abandon the approach altogether.  Use a string stream object for creating formated stings.   Or add operator << functions to your string class to format into a string   (although this is less powerful, unless you are willing to spend storage space on storring precission and other formatting settings in the string object.  However, even without storing settings and just using default settings, its still a pretty good approach.)
Avatar of Axter

ASKER

>>costly in terms of space.
What space???


>>Use a string stream object for creating formated stings.  
>>Or add operator << functions to your string class to
>>format into a string  

The point of the project is to create a set of MFC classes that can work on UNIX, and allow code to be ported over with minimum modifications.
Your approach would violate the requirements.

Much to my supprise, adding the extra Format functions seriouly cut down on the code that required modification.
The format functions are each on line functions.
Example:
void CString::Format(const TCHAR * lpszFormat, CString &src1, CString &src2)
{
     Format(lpszFormat,src1.operator LPCTSTR(),src2.operator LPCTSTR());
}

Adding the ten simple format functions saved me from having to modify hundreds of line's of code.
>> What space???
code takes space.  Plus you have the cost of all those additional entries in the object file, compiler hash tablees, etc that makes code slower to compile and line.   And if this deals with DLLs then you have additional entries in the export table, and that ca ben very costly.  I have a C++ DLL that is about 200k of code and 1 meg of import/export entries.

>> Your approach would violate the requirements.
But you want the impossible.  Not difficult.  Impossible.  Any possible solution is superior to any number of impossible ones.  

Well I suppose its not impossible.  But writting your own C++ compiler is going to be very difficult.

You are going to _have_ to for a subset of features that can be supported on both platforms.  If you rely on VC-specifc behavior, you are not going to have a realistic solution for unix.

>> adding the extra Format functions seriouly cut down on the code that required modification
but they are far from being able to duplicate printf()'s features, which include the ability to format numerical tupes, control padding, leading character, alignment etc etc.
Avatar of Axter

ASKER

>>but they are far from being able to duplicate printf()'s features, which include the ability to format
>numerical tupes, control padding, leading character, alignment etc etc.

You don't understand.  The main Format function has all those features.  The Format function I posted is just a go between the main Format function.

The Format functions with CString is just an overloaded function.

The main Format function uses sprintf, so I don't have to make the stuff you're talking about.
no more notifs please....
>> The main Format function has all those features.  
>> The Format function I posted
>> is just a go between the main Format function.
No I don't understand.   How woudl you format somethign that contains, a number and a string with the above stuff?
Avatar of Axter

ASKER

On the the following code, FormatV is the main Format function.
void CString::Format(const TCHAR * lpszFormat, ...)
{
//     ASSERT(AfxIsValidString(lpszFormat));
     va_list argList;
     va_start(argList, lpszFormat);
     FormatV(lpszFormat, argList);
     va_end(argList);
}

void CString::FormatV(const TCHAR * lpszFormat, va_list argList)
{
     TCHAR * TmpStr = new TCHAR[MaxFormatStringLen];
     ASSERT(TmpStr!=NULL);
     if (sizeof(TCHAR)>1)
     {
#ifdef _GPP_COMPILER_
          vsprintf((char *)TmpStr,(const char *)lpszFormat, argList);
#else
          vswprintf((TCHAR_WCS *)TmpStr,MaxFormatStringLen, //In VC++ the size_t (MaxFormatStringLen) variable is not used
               (const TCHAR_WCS *)lpszFormat, argList);
#endif //_GPP_COMPILER_
     }
     else
     {
          vsprintf((char *)TmpStr,(const char *)lpszFormat, argList);
     }
     CString__assign(TmpStr);
     delete [] TmpStr;
}
Avatar of Axter

ASKER

peterchen,
What is notifs???
I don't see how that works.

>> What is notifs???
question notifications.
Avatar of Axter

ASKER

>>I don't see how that works.
It works, I've tested it to the max.

The only problem I have is that the g++ compiler does not support vsprintf that uses size_t variable.
The vsprintf with the size_t variable is a non-ANSI function found in many Unix compilers which allows you to specify the maximum length of the buffer.
int vsprintf(
          wchar_t *wstr,
          size_t n,
          const wchar_t *format,
          va_list printarg);

With out being able to specify the maximum length, it's possible for the buffer to be over-run.

I've read some where that they're considering adding size_t version of vsprintf to the ANSI standards.
>> I've tested it to the max.
???

how do you format a number and a string at the same time?

i.e.

sprintf(" %s %i",somestring,someint);


>> I've read some where that they're considering adding
>> size_t version of vsprintf to the ANSI standards.
maybe to the ANSI C standards.  Certainly not to the ANSI/ISO C++ standards.  That wouldn't even make it on the agenda.  
Avatar of Axter

ASKER

It uses the same syntax that MFC CString uses.
i.e.

CString Name = "David";
int TestNum1 = 1234;
int TestNum2 = 69;
CString InputBuf ="";
InputBuf.Format("Hello %s.  TestNum1 = %i.  TestNum2 = %i",Name.operator LPCTSTR(),TestNum1,TestNum2);

The only problem is that I have to use operator LPCTSTR(), and that is what I'm trying to get rid of.
As I stated previously, the overloaded Format functions saved me from having to use LPCTSTR() in over 95 percent of the code.  But I would like to have a 100 percent fix for this.

>>not to the ANSI/ISO C++ standards. That wouldn't even make it on the agenda.

Why???
>>not to the ANSI/ISO C++ standards. That wouldn't even make it on the agenda.
Why spend time to "fix" something they don't want people to use?  
notifs = notifications.
I tried to unsubscribe from being notified of this question, but it obviously didn't work.

Good luck with you CStrings ;-)

Peter
Avatar of Axter

ASKER

I'm deleteing this question do to lack of adequate responds.
My solutions has been to overload the printf, sprintf, and CString::Format functions as posted in my (12/14/2000 03:41PM) comment:
That solution allows me to keep the same syntax in my original MFC code.

To All, Thanks for the efforts..
Avatar of Axter

ASKER

I'm deleteing this question do to lack of adequate responds.
My solutions has been to overload the printf, sprintf, and CString::Format functions as posted in my (12/14/2000 03:41PM) comment:
That solution allows me to keep the same syntax in my original MFC code.

To All, Thanks for the efforts....
Avatar of Axter

ASKER

I have found a solution to this question, and I'm going to posted here.
I will then ask a moderator to remove the points, and save this as a PAQ, because I think the information here is useful.
Avatar of Axter

ASKER

My solution is to use template Format() functions and overload them with different quantities of arguments.

The Format function would then call the original MFC Format function but it will call it by using a Translate function that will force the object to convert to one of the built-in types (char*, char, int, long, double, float).

This method will work in any ANSI C++ compiler, and the original source code using the CString::Format function will not have to be modified.

The following message will include the header code for the Format function and the translate functions (zArg()).
Avatar of Axter

ASKER

//Format Functions
private:
      void Format_Main(const TCHAR * lpszFormat, ...);//This is the original MFC-Format function
      //zArg overloaded functions for Format function
      char zArg(char Arg){return Arg;}
      unsigned char zArg(unsigned char Arg){return Arg;}
      int zArg(int Arg){return Arg;}
      unsigned int zArg(unsigned int Arg){return Arg;}
      long zArg(long Arg){return Arg;}
      unsigned long zArg(unsigned long Arg){return Arg;}
      double zArg(double Arg){return Arg;}
      float zArg(float Arg){return Arg;}
      const char* zArg(const char* Arg){return Arg;}
      const char* zArg(const std::string &Arg){return Arg.c_str();}
      LPCTSTR zArg(const CString &Arg){return Arg.operator LPCTSTR();}
public:
      //Unix flavor Format Functions
      //21 Overloaded Format functions
      void Format(const TCHAR * lpszFormat)
      {
            Format_Main(lpszFormat);
      }
      
      template<class T0>
            void Format(const TCHAR * lpszFormat, const T0 &arg0)
      {
            Format_Main(lpszFormat, zArg(arg0));
      }
      
      template<class T0, class T1>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1));
      }
      
      template<class T0, class T1, class T2>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2));
      }
      
      template<class T0, class T1, class T2, class T3>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3));
      }
      
      template<class T0, class T1, class T2, class T3, class T4>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13, const T14 &arg14)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13), zArg(arg14));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13, const T14 &arg14, const T15 &arg15)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13), zArg(arg14), zArg(arg15));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13, const T14 &arg14, const T15 &arg15, const T16 &arg16)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13), zArg(arg14), zArg(arg15), zArg(arg16));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13, const T14 &arg14, const T15 &arg15, const T16 &arg16, const T17 &arg17)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13), zArg(arg14), zArg(arg15), zArg(arg16), zArg(arg17));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17, class T18>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13, const T14 &arg14, const T15 &arg15, const T16 &arg16, const T17 &arg17, const T18 &arg18)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13), zArg(arg14), zArg(arg15), zArg(arg16), zArg(arg17), zArg(arg18));
      }
      
      template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17, class T18, class T19>
            void Format(const TCHAR * lpszFormat, const T0 &arg0, const T1 &arg1, const T2 &arg2, const T3 &arg3, const T4 &arg4, const T5 &arg5, const T6 &arg6, const T7 &arg7, const T8 &arg8, const T9 &arg9, const T10 &arg10, const T11 &arg11, const T12 &arg12, const T13 &arg13, const T14 &arg14, const T15 &arg15, const T16 &arg16, const T17 &arg17, const T18 &arg18, const T19 &arg19)
      {
            Format_Main(lpszFormat, zArg(arg0), zArg(arg1), zArg(arg2), zArg(arg3), zArg(arg4), zArg(arg5), zArg(arg6), zArg(arg7), zArg(arg8), zArg(arg9), zArg(arg10), zArg(arg11), zArg(arg12), zArg(arg13), zArg(arg14), zArg(arg15), zArg(arg16), zArg(arg17), zArg(arg18), zArg(arg19));
      }
      
Avatar of Axter

ASKER

The solution is code heavy, but it gives me the right results.
ASKER CERTIFIED SOLUTION
Avatar of ComTech
ComTech

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Adding to PAQ at Axter's request.

Lunchy
Friendly Neighbourhood Community Support Moderator
Hi Lunchy, looks like we have both been bored without notifs.  :-)

At least I was getting some ntifs put on older Q's so I can get to them later.

Cheers,
CT
Axter,
I came across this interesting old thread in a search.  Did you ever figure out a cleaner implementation?  It seems to me that CString works in a vararg situation because the pointer to the CString object is also a pointer to its string data.
-- Dan
Avatar of Axter

ASKER

>>I came across this interesting old thread in a search.  
>>Did you ever figure out a cleaner implementation?
I never found a cleaner solution.

The original MFC CString code does not work on a UNIX platform consistently.
The above solution works consistently and it's safer.