Jump to content
  • Advertisement
Sign in to follow this  
Quittouff

Base constructor called at the beginning of a derived method?

This topic is 2705 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,
Well almost all is in the title. I had the odd surprise to find out that if I write the following:
class CBar
{
CBar()
{
Trace("CBar Constructor");
}

~CBar()
{
}

void Foo()
{
Trace("Foo");
}
};

class CDerivedBar : CBar
{
CDerivedBar()
{
Trace("Derived Construtor");
}

private void ImNotAnOverrideOfTheBaseClass()
{
}

private void Foo()
{
Trace("Derived Foo");
}
};

//Somewhere do:
CDerivedBar bar;
bar.Foo();

the input is the following:

CBar Constructor
Derived Construtor
CBar Constructor
Derived Foo

So I checked the AS_DEBUG directory where the obj code is generated for CDerivedBar::Foo and there is a line which looks suspicious to me: "2 2 * CALL 432 (CBar::CBar())" (I can paste/send you more if you need).
I tried with an non overridden method (that's the reason of the "ImNotAnOverrideOfTheBaseClass" :P) and same line in the corresponding object file.
Is it a bug or is it an intended behavior?
Now, I would probably not notice it if I didn't do some registering to events in the constructor of my base class, and end up with the registration made a multiple time, actually an infinite time because when the event was called, the constructor was too, registering to the event and so on. I could go with an init mehtod to do the registering so it wouldn't be called several times but then I'd loose the advantage of automatic registration/unregistration through constructor and destructor. That would mean no event if you forget to call init and leak if you forget to call deinit.

Share this post


Link to post
Share on other sites
Advertisement
As you said it looks like a bug, I tried to investigate myself and I found out that it was the fact that methods were private that made a base constructor call, there is no such problem with public methods. It seemed to me that it comes from this part of the code in as_compiler.cpp (around line 228 in version 2.20.2):

//----------------------------------------------
// Examine return type
bool isDestructor = false;
asCDataType returnType;
if( func->firstChild->nodeType == snDataType )
{
returnType = builder->CreateDataTypeFromNode(func->firstChild, script);
returnType = builder->ModifyDataTypeFromNode(returnType, func->firstChild->next, script, 0, 0);

// Make sure the return type is instanciable or is void
if( !returnType.CanBeInstanciated() &&
returnType != asCDataType::CreatePrimitive(ttVoid, false) )
{
asCString str;
str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf());
Error(str.AddressOf(), func->firstChild);
}
}
else
{
returnType = asCDataType::CreatePrimitive(ttVoid, false);
if( func->firstChild->tokenType == ttBitNot )
isDestructor = true;
else
m_isConstructor = true;
}

When it comes at the first if, the node type is snUndefined as it's the private word so it jumps to the else where it sets m_isConstructor to true. Then some lines later, the compiler adds the default constructor call to base class as it thinks it's compiling a constructor.


Now I don't know the code and I don't know if it's the right way to handle the problem but here is a proposition of a fix that seems to work. (I'm working with the Release 2.20.2 so line# may have changed)

in as_scriptnode.h in the enum eScriptNode I added at the end:

snPrivate


in as_parser around line 821
replace:
if( isMethod && t1.type == ttPrivate )
{
node->AddChildLast(ParseToken(ttPrivate));
if( isSyntaxError ) return node;
}

by:
if( isMethod && t1.type == ttPrivate )
{
asCScriptNode* pNode = ParseToken(ttPrivate);
pNode->nodeType = snPrivate;
node->AddChildLast(pNode);
if( isSyntaxError ) return node;
}

and last in as_compiler.cpp around line 228
replace:
//----------------------------------------------
// Examine return type
bool isDestructor = false;
asCDataType returnType;
if( func->firstChild->nodeType == snDataType )
{
returnType = builder->CreateDataTypeFromNode(func->firstChild, script);
returnType = builder->ModifyDataTypeFromNode(returnType, func->firstChild->next, script, 0, 0);

// Make sure the return type is instanciable or is void
if( !returnType.CanBeInstanciated() &&
returnType != asCDataType::CreatePrimitive(ttVoid, false) )
{
asCString str;
str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf());
Error(str.AddressOf(), func->firstChild);
}
}
else
{
returnType = asCDataType::CreatePrimitive(ttVoid, false);
if( func->firstChild->tokenType == ttBitNot )
isDestructor = true;
else
m_isConstructor = true;
}

by:

//----------------------------------------------
// Examine return type
bool isDestructor = false;
asCDataType returnType;

asCScriptNode* nodeToProcess = func->firstChild;
if( func->firstChild->nodeType == snPrivate )
{
if( func->firstChild->next )
{
nodeToProcess = func->firstChild->next;
}
}

if( nodeToProcess->nodeType == snDataType )
{
returnType = builder->CreateDataTypeFromNode(nodeToProcess, script);
returnType = builder->ModifyDataTypeFromNode(returnType, nodeToProcess->next, script, 0, 0);

// Make sure the return type is instanciable or is void
if( !returnType.CanBeInstanciated() &&
returnType != asCDataType::CreatePrimitive(ttVoid, false) )
{
asCString str;
str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf());
Error(str.AddressOf(), nodeToProcess);
}
}
else
{
returnType = asCDataType::CreatePrimitive(ttVoid, false);
if( func->firstChild->tokenType == ttBitNot )
isDestructor = true;
else
m_isConstructor = true;
}





Once again I only tested with my limited use of it, it's NOT AN OFFICIAL fix, just a proposition for witchLord to review, in any cases, I hope it'll help.


P.S: If there is any better way to submit the changes for you to review, please let me know

Share this post


Link to post
Share on other sites
Thanks for the investigation. The fact that it only happens for private methods explains why the bug hasn't been discovered before, as private methods is still quite new and probably not widely used yet (most scripts probably won't ever use it). I'll try to have this fixed still today.

Share this post


Link to post
Share on other sites
The bug has been fixed in revision 848.

Your solution was correct, though there was no reason to create the snPrivate enum, as the tokenType in the asCScriptNode is already there to be used for this purpose. :)


Thanks a lot for the help.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!