Question

odd behavior

Asked by: Salte

Hello fellow experts. I usually answer questions here but right now I have an odd situation and I wonder if anyone know how to get around it easily:

1. I have a class SString which implements a string type class similar to std::string but enough differences that I cannot easily change to std::string.

2. In some code I have a declaration like this:

SString a;

well actually, the variable isn't a but that ought to be irrelevant what the variable name is ;)

3. Checking the assembly code I have found that the compiler appear to be thinking along the lines like this:

a) I want to constract an object a using the default constructor. This is essentially a call to:

a.SString::SString();

which in g++ translates to a call to SString::SString(SString * const this) with a parameter of 'a', so:

SString::SString(& a);

Thus, the constructor get a pointer to the memory block where it should construct the new object.

However, here comes the snag.

b) g++ thinks that since sizeof(SString) == sizeof(void *) == sizeof(int) that it should appearantly treat the struct like a builtin type and transfer it by value. This is normally a good thing since that is what I normally want and it is exactly why the SString type is designed the way it is - I normally do want transfer by value and that the type is treated like a builtin type.

Therefore, instead of transferring a pointer to a, the call transfers the value of a, which happens to be the value stored where the pointer to the string would be stored when the object is constructed. Since the object isn't constructed yet, the data transferred to the constructor is just random garbage. That should be ok since the constructor is to construct a value anyway and transfer it back so you can overwrite that random garbage with a new value.

c) Problem is that the constructor appear to be completely ignorant about the "transfer by value" choice made at the call and assumes that the constructor always get a pointer to the object as argument.

d) The result is that the random garbage value is then taken as the pointer to where the SString object should be constructed and the constructor returns.

e) The caller appear to have forgotten that it transferred the string object by value to a constructor and therefore ought to store a constructed value in its place and moves happily along with garbage value in the SString object.

I would assume that the "transfer by value" is normally a good thing BUT NOT IN A CONSTRUCTOR. It should therefore have been disabled for those and it should have taken the address of the object and called the constructor. This is obviously what the constructor code appear to assume and this is what it should have done.

I believe this is a clear example of a bug in g++. gcc --version displays:

$ gcc --version
gcc (GCC) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
Copyright (C) 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Question: Does anyone know of a workaround for this bug?

No, I cannot change to std::string. That would imply a major overhaul of the program and the program is around 200k lines and I am not going to do that in the near future. We do have plans to switch to STL but not for the immediate future.

Although I can do it, I am VERY reluctant to include phony elements in the SString class just to make it so large that the "transfer by value" semantics is not used. In fact I carefully crafted the class exactly because I want it to use that semantics for the major part of the program. The SString class is used many places and stored and copied around and a transfer by value means that things are faster and simpler than if a traditional transfer by pointer semantics is in use. I would very much welcome some option or trick to force the compiler to disable transfer by value semantics for constructors though. It is obvious that the constructors assume that such semantics is disabled for them, it's just that the caller appear to not be aware of handling constructors any different from any others.

If anyone can point me to where in the gcc source this decision is made so that I can either apply a bug fix or fix it myself, that too, would be welcome.

Thank you in advance for any help you can provide.

Alf

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2003-09-13 at 05:22:17ID20737756
Tags

sstring

Topic

C++ Programming Language

Participating Experts
7
Points
500
Comments
47

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. STL collection classes
    Is it possible to store pointers to objects in the STL collection classes and have the collection automatically destroy the objects as the collection elements are destroyed? e.g.: class A { int var1; }; vector<A*> v; A* obj1 = new A; A* obj2 = new A; v.push_back(o...
  2. Custom Sort Routine for an STL Container?
    How do I pass a custom sort routine to an STL container in VC++? I know that I can simply implement the < operator in my element class, but what if the element is a pointer and I want the ordering to be based on what that pointer points to? My friend suggested the foll...
  3. STL
    Why prefix is better than postfix in STL?

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: rstaveleyPosted on 2003-09-13 at 06:12:32ID: 9352628

I may not be completely with you, Alf, but it sounds like you are depending on a shallow copy because you've not supplied a copy constructor. The shallow copy is dodgy if the object being copied has pointers which are deleted by the class's destructor.

 

by: AxterPosted on 2003-09-13 at 06:40:15ID: 9352718

>>I would assume that the "transfer by value" is normally a good thing BUT NOT IN A CONSTRUCTOR. I

Transfer by value, is normally a bad thing, unless the value is of int size or less.

Why do you think transfer by value is normally good?

I still don't understand what you're trying to accomplish with your constructor.

Does your SString class derive from std::string?

 

by: SaltePosted on 2003-09-13 at 06:50:24ID: 9352748

I do have a copy constructor and must have one since the pointer element is an element where I keep a reference count.

The class is something like this (I have removed several functions which are irrelevant here).

class SString {
private:
    char * M_s;

    static SString snull;

    // Special constructor fo snull.
    SString(int *);

public:

       SString();
       SString(const SString & s);
       SString(const char * s);
       ~SString();

        // This function is public but in the program only
        // a few selected places actually call them.
        // this one is called if you ever store a copy
        // of M_s anywhere. Dire things will happen
        // if s below is not a SString M_s string.
        static void addref(char * s);
        static void subref(char * s);

     ....lots of other functions....
};

In the .cxx file I have some code like this:

struct stringdata_t {
     int refs;
     int length;
     int size;
};

#define GETDATAFROMSTRING(s) (reinterpret_cast<stringdata_t *>(s) - 1)
#define GETSTRINGFROMDATA(d) reinterpret_cast<char *>((d) + 1)
#define ALLOCDATAFORSTRING(len) reinterpret_cast<stringdata_t *>(xmalloc(sizeof(stringdata_t) + 1 + (len)))
#define ADDREFSTRING(s) \
     do { \
         GETDATAFROMSTRING(s)->refs++; \
    } while (0)
#define SUBREFSTRING(s) \
    do { \
          if ((s) != NULL) { \
             stringdata_t * temp = GETDATAFROMSTRING(s); \
             if (--(temp -> refs) == 0) \
                      xfree(temp); \
         } \
    } while (0)

void SString::addref(char * s)
{
    ADDREFSTRING(s);
}

void SString::subref(char * s)
{
    SUBREFSTRING(s);
}

// Special constructor to build snull.
SString::SString(int *)
{
    stringdata_t * p = ALLOCDATAFORSTRING(0);
     p -> refs = 1;
     p -> size  = 1;
     p -> length = 0;
    M_s = GETSTRINGFROMDATA(p);
}

// Default constructor.
SString::SString()
   : M_s(snull.M_s)
{
    ADDREF(M_s);
}

// Copy constructor
SString::SString(const SString & s)
    : M_s(s.M_s)
{
   ADDREF(M_s);
}

// construct an SString from a char * string.
SString::SString(const char * s)
   : M_s(snull.M_s)
{
    if (s != NULL) {
        size_t len = strlen(s);
         stringdata_t * p = ALLOCDATAFORSTRING(len);
         p -> refs = 1;
         p -> size = len + 1;
         p -> length = len;
        M_s = GETSTRINGFROMDATA(p);
        strcpy(M_s,s);
    } else {
        ADDREF(M_s);
    }
}

SString::~SString()
{
    SUBREF(M_s);
}

Ok, so it's not exactly like above. For example when we allocate buffer for a string the size isn't exactly length + 1 but can be larger. For "small" strings we have a set of "suitable sizes" and we enlarge the size to fit one of those. For large strings we allocate length + 1.

However, the string class works fine - that is really the odd thing, it generally works fine. it is just that this particular place the compiler insist on translating:

SString contact;

so that if I in the debugger display & contact  == XXXXXX and contact.M_s  = YYYYYY and when I enter the constructor SString::SString I see that this is equal to YYYYY and not XXXXX as it should be.

What really freaks me out is that it appears to work fine just about anywhere else... We use this class a lot so I am sure that if it was the class that had a problem we would have seen more problems happening. I am fairly sure it is the place of the call to the constructor that is at fault and not the class itself. However, if I can do something to the class so that it doesn't do this it would solve the problem.

Alf

 

by: SaltePosted on 2003-09-13 at 06:55:03ID: 9352764

Because the class happen to be of size int (well, void * to be more exact).

For such 'small' classes it is normally a good thing that you have transfer by value. It avoids a lot of indirect references, no need to worry about memory allocation (keep it in registers) etc, etc...

The problem arise if caller and callee do not agree on whether it is used or not.

Alf

 

by: jkrPosted on 2003-09-13 at 07:51:27ID: 9353031

Hmm, stuptid question: Do you have any optimizations turned on?

 

by: rstaveleyPosted on 2003-09-13 at 08:29:07ID: 9353197

So, I understand that the declaration...

    SString contact;

...calls the default contructor...

   SString::SString()  : M_s(snull.M_s)
   {
       ADDREF(M_s);
   }

...which increments the stringdata_t reference count in your static SString snull, which itself was constructed by SString::SString(int *) for obvious reasons.

What I don't understand is what you are seeing in your debugger. Do you see constact.M_s point to something other than the address of stringdata_t pointed to by snull.M_s after construction, or does the problem arise later in your code?

 

by: rstaveleyPosted on 2003-09-13 at 08:39:42ID: 9353245

Oh hang on... ADDREF isn't defined in the code you posted. Did you mean to put ADDREFSTRING?

 

by: rstaveleyPosted on 2003-09-13 at 08:45:46ID: 9353274

I mean...

#define ADDREF(p) \
        ((reinterpret_cast<stringdata_t *>(p))->refs++)
#define SUBREF(p) \
        ((reinterpret_cast<stringdata_t *>(p))->ref--)


??

 

by: rstaveleyPosted on 2003-09-13 at 09:01:54ID: 9353350

Does the problem appear with your compiler in the following trivial program:

--------8<--------
#include <iostream>
#include <memory>

#define xfree   free
#define xmalloc malloc

class SString {
private:
   char * M_s;

   static SString snull;

   // Special constructor fo snull.
   SString(int *);

public:

      SString();
      SString(const SString & s);
      SString(const char * s);
      ~SString();

       // This function is public but in the program only
       // a few selected places actually call them.
       // this one is called if you ever store a copy
       // of M_s anywhere. Dire things will happen
       // if s below is not a SString M_s string.
       static void addref(char * s);
       static void subref(char * s);

//    ....lots of other functions....
};

struct stringdata_t {
    int refs;
    int length;
    int size;
};

#define GETDATAFROMSTRING(s) (reinterpret_cast<stringdata_t *>(s) - 1)
#define GETSTRINGFROMDATA(d) reinterpret_cast<char *>((d) + 1)
#define ALLOCDATAFORSTRING(len) reinterpret_cast<stringdata_t *>(xmalloc(sizeof(stringdata_t) + 1 + (len)))
#define ADDREFSTRING(s) \
    do { \
        GETDATAFROMSTRING(s)->refs++; \
   } while (0)
#define SUBREFSTRING(s) \
   do { \
         if ((s) != NULL) { \
            stringdata_t * temp = GETDATAFROMSTRING(s); \
            if (--(temp -> refs) == 0) \
                     xfree(temp); \
        } \
   } while (0)

void SString::addref(char * s)
{
   ADDREFSTRING(s);
}

void SString::subref(char * s)
{
   SUBREFSTRING(s);
}

// Special constructor to build snull.
SString::SString(int *)
{
   stringdata_t * p = ALLOCDATAFORSTRING(0);
    p -> refs = 1;
    p -> size  = 1;
    p -> length = 0;
   M_s = GETSTRINGFROMDATA(p);
        std::cout << "int* constructor called\n";
}

#define ADDREF(p) \
        ((reinterpret_cast<stringdata_t *>(p))->refs++)
#define SUBREF(p) \
        ((reinterpret_cast<stringdata_t *>(p))->refs--)

// Default constructor.
SString::SString()
  : M_s(snull.M_s)
{
   ADDREF(M_s);
        std::cout << "Default constructor called\n";
}

// Copy constructor
SString::SString(const SString & s)
   : M_s(s.M_s)
{
  ADDREF(M_s);
        std::cout << "Copy constructor called\n";
}

// construct an SString from a char * string.
SString::SString(const char * s)
  : M_s(snull.M_s)
{
   if (s != NULL) {
       size_t len = strlen(s);
        stringdata_t * p = ALLOCDATAFORSTRING(len);
        p -> refs = 1;
        p -> size = len + 1;
        p -> length = len;
       M_s = GETSTRINGFROMDATA(p);
       strcpy(M_s,s);
        std::cout << "Constructed from non-NULL char*\n";
   } else {
       ADDREF(M_s);
        std::cout << "Contructed from NULL char*\n";
   }
}

SString::~SString()
{
   SUBREF(M_s);
        std::cout << "Destructor called\n";
}

SString SString::SString::snull((int*)0);

int main()
{
        std::cout << "main entered\n";
        {
                SString contact;
                std::cout << "contact constructed\n";

                SString contact2("Fred");
                std::cout << "contact2 constructed as \"Fred\"\n";

                SString contact3(contact2);
                std::cout << "contact3 contracted from contact2\n";

        }
        std::cout << "contacts all destructed\n";

        std::cout << "main exiting\n";
}
--------8<--------

 

by: DanRollinsPosted on 2003-09-13 at 15:55:45ID: 9354625

I suggest that you have look at the source code for MFC's CString class.   The brains at Microsoft have confronted these issues and solved them all.  Perhaps examining their code will jar something loose.

They call an Init() fn in every constructor that presets the CStringData structure to a predefined value, including a reference count of -1.

-- Dan

 

by: AxterPosted on 2003-09-13 at 17:08:37ID: 9354789

Salte,
Your explanation is still ambiguous.  You usually do such a good job of giving long detailed answers for other member's questions, but in your question here, you seem to be missing the important details.

Can you post an example code that uses you're SString class, and that gives you the undesired results?
Something like this:
SString Data1 = "Data needed to reproduce error";

SString Data2 = Data1; //Format that produces error, and comment stating the erroneous results

Try to keep it simple, an comment the point at which you get the undesired results.

 

by: AxterPosted on 2003-09-13 at 17:14:36ID: 9354806

I think I now see what you're trying to accomplish.

Correct me if I'm wrong, but you're trying to create a default SString so that when an EMPTY SString object is created, all you have to do is point to the snull object, instead of allocating memory for a new SString object.

If this is the case, then I agree with DanRollins, in that you should look at the CString class.
The current method of this class has a lot of potensial problems with using snull.

Please verify if your current problem is associated with the creation of your snull object.

 

by: DanRollinsPosted on 2003-09-13 at 17:26:51ID: 9354844

Note that some of the code for CString is in AFX.H and AFX.INL as well as STRCORE.CPP and STREX.CPP
-- Dan

 

by: AxterPosted on 2003-09-13 at 17:38:14ID: 9354887

I just looked at your code some more, and there are diffinitely problems with the use of snull.

snull is a static member, which is created in a seperate tranlation unit.
According to the C++ standard, there's no method for garanteeing the order in which none-POD objects are created in different translation units.

So say you have the following:

//In SString.cpp
SString SString::SString snull((int*)NULL);


//In main.cpp

SString MyString;

int main(int, char*)
{
  cout << MyString;
  return 0;
}

This code is considered to have undefined results, because there's no garantee that snull will be created before MyString is created.
If MyString is initialized first, then M_s will be pointing to sum random value.

SString::SString()
 : M_s(snull.M_s) //Since snull is not yet initialized, this can be pointing to anything

continue....

 

by: AxterPosted on 2003-09-13 at 17:42:15ID: 9354896

The way Microsoft got around this problem, was by initializing a CString object to a POD type.
This works, because the standard does garantee that build-in types get initialized before custom types, and POD types get initialized before custom types (types with constructors).

This is the method I recommend you use.

If you need help with setting it up, I can post an example.

 

by: AxterPosted on 2003-09-13 at 19:21:18ID: 9355224

Here's a workaround solution:

class SString {
private:
   char * M_s;

   static SString &snull; //Make this a reference

***********************
In cpp file:

struct stringdata_t {
   int refs;
   int length;
   int size;
};


stringdata_t DataForSnull= {1,1,0};
SString SString::SString &snull = (SString)GETSTRINGFROMDATA(&DataForSnull);


This should work because DataForSnull is a POD type, and snull is a reference.
Both type will get initialized before any other custom type, which will insure you're snull is initialized before any other SString object.

 

by: AxterPosted on 2003-09-13 at 19:27:20ID: 9355251

From your original question:
>>d) The result is that the random garbage value is then taken as the pointer to where the SString object should be constructed and the constructor returns.

The random garbage is most likely due to the object been initialized in seperate translation unit.
So to answer your original question, what the debugger is showing you is the multi translation unit problem previouly described, and it has nothing to do with the compiler picking the wrong constructor.

 

by: DanRollinsPosted on 2003-09-13 at 23:54:01ID: 9356063

On the other hand, Microsoft is the *actual* evil empire, so maybe you should not emulate anything that they have done.

 

by: NickJohnsonPosted on 2003-09-14 at 00:34:17ID: 9356152

Salte --

// Special constructor to build snull.
SString::SString(int *)
{
   stringdata_t * p = ALLOCDATAFORSTRING(0);
    p -> refs = 1;
    p -> size  = 1;
    p -> length = 0;
   M_s = GETSTRINGFROMDATA(p);
}

This is already handled by the void-parameter-list version of the constructor. Can you live without this?  b/c it seems that your reported mis-overloading of the constructor may be related to this.  Comment that out...

Peace,
Nick

 

by: SaltePosted on 2003-09-14 at 02:14:28ID: 9356299

First off,

I think I have resolved the problem. The code is sort of doing the right thing.

The declaration is fine, it's just that since that string is returned, the compiler appear to do it like this:

Ok, he declares a string and then returns it. Since the function have SString return type I translate that into a hidden pointer to SString parameter as input, so the function:

SString SomeClass::func(int x);

is translated into:

void SomeClass_func(SString * retval, SomeClass * const this, int x);

The declaration is ignored, since it is only used to build the return type and whenever my code refer to 'contact' it translates it into a reference to 'retval' since retval is a pointer to string the compiled code does the right thing.

I should have guessed it was something like that since the location of that value was in a positive offset from EBP - local variables are stored in negative offsets from EBP while arguments have positive offsets.

It does puzzle me though still since the call is like:

SString some_str = SomeClassObj -> func(x);

and the &some_str is NOT the value passed in as argument to func....

Intstead the code appearantly creates an additional temp in the callers stack frame and it is the value of that SString that is passed as argument. I hope that when I turn on optimization that it removes that utterly useless compiler generated temporary SString variable.

The debugger still get it wrong though since the debugger appearantly treat 'contact' as a regular SString variable and not as a pointer to string as it should but I guess that is a gdb bug and not a gcc bug. What is really going on is that the compiler essentially ignore the 'contact' declaration and pretend that variable wasn't declared and whenever I use 'contact' in the code the compiler translates it to *retval and whenever &contact is called for it uses retval. However, the debugger, gdb, is obviously unaware of all this and pretend that contact exist and that is the cause of the problems.

Anyway, thank you for any help you guys offered and it at least gave me a nudge to look in the right direction. Not sure how to distribute the points though...I might split them between ya.

Alf

 

by: rstaveleyPosted on 2003-09-14 at 03:19:13ID: 9356370

Not really with you, Alf, but SString should indeed be allocated in the calling function otherwise it could be clobbered by the next thing to be pushed on the stack after the function returns. EDX:EAX should point to it when the function returns (assuming you are talking 32-bit Intel).

 

by: rstaveleyPosted on 2003-09-14 at 03:21:21ID: 9356373

> EDX:EAX should point to it when the function returns (assuming you are talking 32-bit Intel).

I mean 64 bit - doh.

Read EAX for 32-bit.

 

by: rstaveleyPosted on 2003-09-14 at 03:24:13ID: 9356378

Were you hoping/expecting the SString object to be returned entirely in registers?

 

by: SaltePosted on 2003-09-14 at 03:45:22ID: 9356419

Well, as I found out it wasn't a problem with gcc at all.

The problem is with gdb.

gcc see the following declaration.

SString SomeClass::func(...params)

and internally in gcc this is translated to:

void func(SString * returnvalue, SomeClass * const this, ....params)

This function is written along these lines:

SString SomeClass::func(...params)
{
     SString contact;
     SString host;

    ....some code here...
   return contact;
}

Now. since the compiler translates the return to be a hidden argument, and the contact parameter is used as return value, you don't really need it - just use *returnvalue instead, right?

So the compiler essentially let 'contact' silently disappear although it tells the debugger that 'contact' is the return value, so the declaration is like this:

void func(SString contact, SomeClass * const this, ...params)

And here is a bug or misunderstanding between gcc and gdb. gcc are aware that this is really a pointer to a string and generates code for that, so it correctly loads returnvalue which holds the address of contact, thus gcc treat the function declaration as if it was:

void func(SString & contact, SomeClass * const this, ...params);

but somehow gdb get this wrong and thinks that the declaration is:

void func(SString & contact, SomeClass * const this, ...params);

and so when I want to dump the contents of contact I see that contact.M_str is some address. This is really the address of contact but gdb appear to get a level of indirection wrong.

since I got the wrong value in gdb I assumed first that it was a bug in the compiler and so I posted the question here.

It is really just a bug in gdb and it doesn't know how to print the value correctly, so I just ignore it There's no work-around needed.

Kinda hard though to get it right I guess, since contact is declared as a SString so gdb really have to see it that way, it's just that where SString is located is not at EBP+8 but it is that EBP+8 contain a pointer to it. Appearantly gdb is unable to handle that situation. It could be fixed by having gdb see the type of contact to be a reference to SString but that would confuse most users who would then see that they declare contact as a SString and gdb report it as SString &.

Best way would be to just report the type as 'SString' but internally know that the actual type is really 'SString *' or 'SString &', but this is hidden from user.

However, as it is a gdb bug I don't really worry about it any more.

Alf

 

by: AxterPosted on 2003-09-14 at 04:25:44ID: 9356504

Salte,
Your code is still going to have a problem with snull not being initialized before it gets used when you have any global or static member SString objects.

 

by: SaltePosted on 2003-09-14 at 04:34:31ID: 9356529

Well, snull is initialized before it gets used - I am very sure of that ;)

It was hard work to ensure that and it probably would be better to use the STL method of having an object that doesn't
require constructor at all so if I were to write the code over again I won't do it the way I did.

However, the snull constructor is the first user-level constructor called - It has the lowest priority number (gcc call constructors with lower priority number before any other and those without any explicit priority get a high value so they are called late).

All my key declarations have such priorities attached to them and have to have that, snull is just one of them.

The reason is that I have to have very exact sequence on when things from different compilation modules are constructed so I have the __attribute__ ( __priority__( ....) ); on several of my declarations.

It was a pain to type all that in but it's done and it works so I am not going to change that in the near future.

And I have lots of global and static SString objects but they are all constructed after snull.

Alf

 

by: SaltePosted on 2003-09-14 at 04:35:39ID: 9356534

I admit that I would probably have written the code differently today using other ways to achieve the same result but the code is written and it works so I won't bother with it.

Alf

 

by: AxterPosted on 2003-09-14 at 04:41:16ID: 9356551

>>The reason is that I have to have very exact sequence on when things from different compilation modules are constructed so I have the __attribute__ ( __priority__( ....) ); on several of my declarations.

Ahh!
You left this out, so I wasn't aware of it.  Actually I didn't even know that GCC had that feature.

 

by: SaltePosted on 2003-09-14 at 05:11:30ID: 9356607

It does and it's handy but it of course makes the code non-portable.

Fortunately, all the priority declarations are placed in some macros so it's not a big deal to write it for a new system that uses slightly different syntax or which doesn't allow for priority (although in the latter case the code will have serious problems to run).

And about leaving out things. Yes, I have left out much of the code which I considered irrelevant for the problem at hand.

The problem was that I did

p contact

in gdb and got a value which was "garbage" and I so I started to check the assembly code and found that the compiler treat the value as a pointer to SString and not as an SString as I had expected it to. At first I misunderstood this and thought that it did a transfer by value but that wasn't the case at all. What was the case was that it simply left out the declaration since it is only used to build a return value which is then returned. Since the return value is reached through the data space given by the function call it makes sense.

I also think I know why it uses a separate temporary space and not a pointer to the SString variable I store the return value into. The return value is of course an object which isn't constructed while the object receiving the return value is already constructed. So it cannot give a pointer to that, instead it build a temporary object in caller's stack frame, provide a pointer to it. Then the function construct an SString in that object and then the caller can copy the SString built there into the variable I have declared for it by a copy constructor it then call a destructor for the temporary object (callee construct it and caller destroys it).

Oh well, case is closed for my part in the sense that I found that it is simply an error in the way gdb display the value - not in the way gcc handle it. The only thing left is how to distribute points. I sort of solved it myself but I did get some valuable input from various people so I will probably split the points to some posters here ;)

Alf

 

by: GaryFxPosted on 2003-09-14 at 05:44:50ID: 9356688

Repeat after me:

   gdb cannot be trusted
   gdb cannot be trusted
....

The quality of gdb doesn't come close to the quality of gcc/g++.

Gary

 

by: SaltePosted on 2003-09-14 at 07:19:24ID: 9356925

I sort of knew that already. It's not that long ago that I read an article where the gcc developers had slammed gdb for lacking vital functionality for supporting a language such as C++. For example scoping, namespaces etc have horrible support in gdb. Every time I try to do a

p foo::bar

and get that there is no 'bar' defined anywhere when I know very well that there is a static member bar defined in class foo, or a member bar defined in namespace foo etc...

So, I sort of knew that already. It does surprise me that it cannot even handle this properly.

I guess nothing much is done with gdb in the last decade because nobody wants to say "I am working on gdb", it will just cause them to get yelled at since gdb is so bad, it's not much to be proud of so to speak.

What alternatives exist for gcc under linux?

Alf

 

by: GaryFxPosted on 2003-09-14 at 07:58:56ID: 9357073

Alternatives to gcc on Linux:  Perl, Python, Java, etc.

Or were you expecting a serious answer?  There's probably an Intel compiler for Linux, but I don't know of any alternative to gdb.

Gary

 

by: SaltePosted on 2003-09-14 at 10:33:58ID: 9357655

I don't need alternatives to gcc, that's just fine... it's alternatives to gdb I was asking for...

Perl is just fine, Java too but not for the projects I use now. I don't have much experience with Python although I know the basics features of the language (indentation determine block level). It's essentially enough to make me get shivers... ;)

Alf

 

by: AxterPosted on 2003-09-14 at 11:24:18ID: 9357896

>>What alternatives exist for gcc under linux?
The alternative is to have a good  debug logger.

 

by: SaltePosted on 2003-09-15 at 00:09:38ID: 9360826

I goofed and said:
>> What alternatives exist for gcc under linux?

OOops, I just realize that the cause of the misunderstanding is my own.

I meant to write:

What alternatives to gdb exist under Linux.

That was just a screw-up in my brain there I guess... I never wanted alternatives to gcc, gcc is just fine ;)

It's gdb that is the problem.

Sorry about that.

Alf

 

by: rstaveleyPosted on 2003-09-15 at 00:28:22ID: 9360887

> What alternatives to gdb exist under Linux

Being infallible? ;-)

Getting gcc's assembler listing with source code comments is handy.

e.g.

   gcc -Wa,-ahls=MyModule.lst -c -g -o MyModule.o MyModule.cpp

 

by: DanRollinsPosted on 2003-09-15 at 00:48:51ID: 9360984

What I don't understand is why you stopped looking when you saw the "wrong address" in a stack variable.  Most programmers would have single-stepped a few times and seen that things get straightened out a few steps later (I'm guessing that's how you eventually solved the problem).

-- Dan

 

by: SaltePosted on 2003-09-15 at 01:16:31ID: 9361118

Dan,

Probably because it just didn't triggered in my brain at the time.

I saw the disassembly via gdb and saw that it loaded [EBP+8] and that should have triggered as you say. It just didn't. I was fairly tired, been working all weekend to get something fixed by today (most of the things seems to be ok now, but I still have some testing to do before I am all set so I am kinda behind schedule). Anyway, I did eventually stop and think over
that odd offset and thus found the solution, I just didn't do it immediately and it did take me a while before I did.

I just got shocked when I saw the very wrong value in the SString variable and then it was returned... and I just never came to examine the returned string to see that it was actually ok.

Ok, so I goofed. Should I just go and shoot myself and get it over with? For those of you who've read Douglas Adams you might recognize the line: Should I stand in a corner and rust or just fall apart where I'm standing? (That was one of Marvin's famous lines)

No, don't worry, I have no immediate plans for suicide just because I goofed in a debugging session.

Alf

 

by: AxterPosted on 2003-09-15 at 07:32:59ID: 9363230

Salte,
Just out of curiousity, why didn't you just derive your class from std::string, at let it do the reference counting work?

 

by: DanRollinsPosted on 2003-09-15 at 13:37:01ID: 9365944

Hey let's all jump on Salte! :)

After I posted, I realized how much we depend on our tools.  If it is hard to saw through a piece of wood with a new handsaw, our first thought is that the wood is extra hard, not that the sawblade is dull.

I recommend using Microsoft tools for all projects.  That is becasue they have been tested by millions of really grouchy programmers.  Who can one hold responsible when some "free" debugger upchucks garbage?

 

by: SaltePosted on 2003-09-16 at 02:26:31ID: 9369091

Dan,

I doubt that MS tools will run on this linux machine.....

Alf

 

by: AxterPosted on 2003-09-16 at 05:19:47ID: 9369941

>>I doubt that MS tools will run on this linux machine.....

As much as I complain about MS VC++6.0 not being C++ compliant, I still loved it's IDE,  and when I had to developed and debug a major C++ project on UNIX, I found it hard to depart from it.
So I created an Add-In for VC++ 6.0, that allowed me to launch the GNU Compiler and the GNU debugger from VC++ IDE via telnet.
I was able to step through the code and put break marks.  I was also able to select a variable, and then click on a button to have the contents displayed.

The Add-In didn't always work perfectly, but it was better then using command line options and remembering all the required GDB commands.

So, in a since, you can use MS tools with linux environment.

 

by: SaltePosted on 2003-09-16 at 05:30:52ID: 9370010

Well, but that still uses gdb as debugger on linux and it is gdb that is the problem here. I use 'make' when I compile the program so command line options are never a problem.

Alf

 

by: tinchosPosted on 2004-01-26 at 17:29:11ID: 10205843

This question has been classified as abandoned.  I will make a recommendation to the moderators on its resolution in approximately one week.  I would appreciate any comments by the experts that would help me in making a recommendation.

It is assumed that any participant not responding to this request is no longer interested in its final deposition.

If the asker does not know how to close the question, the options are here:
http://www.experts-exchange.com/help.jsp#hs5

Tinchos
EE Cleanup Volunteer

 

by: rstaveleyPosted on 2004-01-27 at 02:23:24ID: 10208043

Axter,

> So I created an Add-In for VC++ 6.0

Have you written a full description of what you did somewhere or other? This sounds interesting.  Sorry to pick up on this, just as tinchos is classifying the question as abandoned, but I missed your comment.

 

by: tinchosPosted on 2004-02-13 at 13:05:21ID: 10356157

No comment has been added lately, so it's time to clean up this TA.
I will leave the following recommendation for this question in the Cleanup topic area:

Split: DanRollins {http:#9360984} & Axter {http:#9369941}

Please leave any comments here within the next four days.
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Tinchos
EE Cleanup Volunteer

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...