By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,708 Members | 1,473 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,708 IT Pros & Developers. It's quick & easy.

static members of template classes optimized away?

P: n/a
Hello,

this is a test case of something I just can't explain myself:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <list>
typedef void (*registerfunc)();

class CMgr
{
public:
static std::list<registerfunc> & GetRegisterList()
{
static std::list<registerfunc> registerlist;
return registerlist;
}

static bool Register(registerfunc func)
{
GetRegisterList().push_back(func);
return true;
}

};

template< class T> class THelper
{
public:
//virtual bool GetDummy() { return ms_bDummy; }

private:
static bool ms_bDummy;
};

template <class T>
bool THelper<T>::ms_bDummy=CMgr::Register(T::Register);
class CTest : public THelper<CTest>
{
public:
static void Register()
{
printf("Registering CTest\n");
}
};

int main(const char * args[], int iNumArgs)
{
printf("There are %d functions registered.\n",
CMgr::GetRegisterList().size());

return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7

I just want a list of functions be generated when the program starts
up. The output of the program is the following:

Optimizations turned off:
"There are 0 functions registered."

Optimizations turned off and "GetDummy()" uncommented in THelper:
"There are 1 functions registered."

Leaving "GetDummy()" uncommented and turing optimizations on:
"There are 0 functions registered."

Instanciating CTest and calling GetDummy() from main():
"There are 1 functions registered."

I cannot explain this to me. When is the compiler optimizing away the
static member and not performing initialization?

Thanks a lot.

Markus

Feb 14 '06 #1
Share this Question
Share on Google+
8 Replies


P: n/a

Markus Henschel wrote:
Hello,

this is a test case of something I just can't explain myself:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <list>
typedef void (*registerfunc)();

class CMgr
{
public:
static std::list<registerfunc> & GetRegisterList()
{
static std::list<registerfunc> registerlist;
return registerlist;
}

static bool Register(registerfunc func)
{
GetRegisterList().push_back(func);
return true;
}

};

template< class T> class THelper
{
public:
//virtual bool GetDummy() { return ms_bDummy; }

private:
static bool ms_bDummy;
};

template <class T>
bool THelper<T>::ms_bDummy=CMgr::Register(T::Register);
class CTest : public THelper<CTest>
{
public:
static void Register()
{
printf("Registering CTest\n");
}
};

int main(const char * args[], int iNumArgs)
{
printf("There are %d functions registered.\n",
CMgr::GetRegisterList().size());

return 0;
}


Members of templates are instantiated on demand. As long as nothing in
the code accesses THelper<T>::ms_bDummy it does not get instantiated.

Feb 14 '06 #2

P: n/a
Right. When I uncomment the virtual function accessing ms_bDummy the
static member is initialized. But optimizations make the compiler
remove GetDummy because it is never called and this in turn removes the
static member. But shouldn't the compiler know that the function that
initializes m_bDummy is changing other variables that ARE accessed?

I had this idea of executing initialization time code from an article
about a property system. I checked it again and the only difference to
my code is that I use a bool as static member but the article used a
property struct. So I made some changes to the example code:

////////////////////////////////
template< class T> class THelper
{
public:
virtual bool GetDummy() { return ms_Dummy.m_bTest; }
static CDummy ms_Dummy;
};

template <class T>
CDummy THelper<T>::ms_Dummy=CMgr::Register(T::Register);
class CTest : public THelper<CTest>
{
public:
static void Register()
{
printf("Registering CTest\n");
}
};
//////////////////////////////////

This way the output is always: "There are 1 functions registerd." Can I
rely on this or is this just because the compiler isn't smart enough to
remove the GetDummy() member function?

Feb 14 '06 #3

P: n/a

Markus Henschel wrote:
Right. When I uncomment the virtual function accessing ms_bDummy the
static member is initialized. But optimizations make the compiler
remove GetDummy because it is never called and this in turn removes the
static member. But shouldn't the compiler know that the function that
initializes m_bDummy is changing other variables that ARE accessed?

I had this idea of executing initialization time code from an article
about a property system. I checked it again and the only difference to
my code is that I use a bool as static member but the article used a
property struct. So I made some changes to the example code:
[]
This way the output is always: "There are 1 functions registerd." Can I
rely on this or is this just because the compiler isn't smart enough to
remove the GetDummy() member function?


You can make your code more reliable by simplifying it.

First, I got rid of std::list<> and replaced it with a custom list
which only requires static initialization and thus is not prone to the
order of dynamic initialization between modules problem. You can
declare an extern object of this class in a header and safely use it
from constructors of global objects. Also, the list is intrusive, so
that it does not require dynamic allocations.

Second, I add elements into the list by declaring global (or namespace
scope) objects of a class. Its constructor adds it into the list and
this can not be optimized out.

struct registrations
{
struct node
{
void(*fn)();
node* next;
};

void push_front(node* n)
{
n->next = head_;
head_ = n;
}

void invoke_all()
{
for(node* n = head_; n; n = n->next)
n->fn();
}

node* head_;
} l = {};

template<void(*FN)()>
struct do_register : registrations::node
{
do_register()
{
this->fn = FN;
l.push_front(this);
}
};

struct some
{
static void register_fn() { printf("%s\n", __PRETTY_FUNCTION__); }
};

do_register<some::register_fn> const register_some;

int main()
{
l.invoke_all();
}

Feb 14 '06 #4

P: n/a
Thanks. So it is not possible to somehow force the initialization of
static members that are not used? It is just out if interest as your
solution of manually registering classes by filling a header file is
just slightly more work but offers more flexibility.

Feb 14 '06 #5

P: n/a

Markus Henschel wrote:
Thanks. So it is not possible to somehow force the initialization of
static members that are not used?


Just take its address.

class CTest : public THelper<CTest>
{
public:
static void Register()
{
void* p = &THelper<CTest>::ms_Dummy; (void)p;
printf("Registering CTest\n");
}
};

Feb 14 '06 #6

P: n/a
Markus Henschel wrote:
Hello,

this is a test case of something I just can't explain myself:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <list>
typedef void (*registerfunc)();

class CMgr
{
public:
static std::list<registerfunc> & GetRegisterList()
{
static std::list<registerfunc> registerlist;
return registerlist;
}

static bool Register(registerfunc func)
{
GetRegisterList().push_back(func);
return true;
}

};

template< class T> class THelper
{
public:
//virtual bool GetDummy() { return ms_bDummy; }

private:
static bool ms_bDummy;
};

template <class T>
bool THelper<T>::ms_bDummy=CMgr::Register(T::Register);
class CTest : public THelper<CTest>
{
public:
static void Register()
{
printf("Registering CTest\n");
}
};

int main(const char * args[], int iNumArgs)
{
printf("There are %d functions registered.\n",
CMgr::GetRegisterList().size());

return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7

I just want a list of functions be generated when the program starts
up. The output of the program is the following:

Optimizations turned off:
"There are 0 functions registered."
Ok. CTest forces THelper<CTest> to instantiate. But non of the member of
THelper<CTest> ever gets used so they are not instantiated. Therefore no
function registration.

Optimizations turned off and "GetDummy()" uncommented in THelper:
"There are 1 functions registered."
Now CTest again forces THelper<CTest> to instantiate. CTest also opens a
chance for other types which inherits CTest to override the virtual
function THelper<CTest>::GetDummy, and that forces
THelper<CTest>::GetDummy to instantiate, which in turn instantiates
THelper<CTest>::ms_bDummy, which gets CTest::register registered.

Leaving "GetDummy()" uncommented and turing optimizations on:
"There are 0 functions registered."
Now the compiler performs whole program optimization and finds out that
the virtual function is never ever used, so optimized it off, taking
ms_bDummy down with it.

The compiler is gonna opt it away even if you explicitly instantiate it
because the optimization happens after all template instantiation. :)

Instanciating CTest and calling GetDummy() from main():
"There are 1 functions registered."
Well if you use GetDummy() then the optimizer of course can't optimize
it off.

I cannot explain this to me. When is the compiler optimizing away the
static member and not performing initialization?
Because it is allowed to optimize away entities that are not used
through out the program. Your trick to register is consider a side
effect and is therefore ignored.

Thanks a lot.

Markus

Regards,
Ben
Feb 14 '06 #7

P: n/a
What does the above (void)p do?

What I really wanted was to be able to execute code automatically at
program startup so it's sufficient to just write a class and it will
automatically insert a classinfo struct into a global list that can be
browsed at runtime. The struct would contain a factory function. So a
class instance can be created without knowing the class.

By deriving from a class template a static member of type struct
classinfo is added to the class. In the constructor of classinfo I
added a pointer to it to a global list.

The problem I'm facing is that the static struct might not be
initialized and created if the class itself is not really used.

Feb 14 '06 #8

P: n/a
> Just take its address.

class CTest : public THelper<CTest>
{
public:
static void Register()
{
void* p = &THelper<CTest>::ms_Dummy; (void)p;
printf("Registering CTest\n");
}
};


This won't work. Guess what? THelper<CTest> IS instantiated. But hey
hello optimizer takes away its members after it is instantiated.

If your optimizer is powerful enough you can actually use
THelper<CTest>::GetDummy in main and still no function gets registered. :)

In my case I need to actually write into THelper<CTest>::ms_dDummy to
get what the OP desires to:

int main(const char * args[], int iNumArgs)
{
CTest t;
t.ms_bDummy = true; // bust the optimizer

printf("There are %d functions registered.\n",
CMgr::GetRegisterList().size());

return 0;
}

Regards,
Ben
Feb 14 '06 #9

This discussion thread is closed

Replies have been disabled for this discussion.