Reputation: 1143
Developing environment GNU GCC (g++) 4.1.2, CMAKE
After I read some authoritative book named 'Advanced C++ Programing Styles and Idioms by James Coplien (I must admit that this book is quite old. So some of you may find it a sort of out-dated.), I found the 'Exemplar idiom' was quite useful to impair the inter-dependency among inheritance class hierarchy. So tried to create simple project for handling this idiom as follows.
Thank you for your patience in advance while I list the entire source code as follows.
In CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(TestProject)
add_library(exemplar_idiom STATIC base.cpp derived1.cpp)
add_executable(exemplar_example main.cpp)
target_link_libraries(exemplar_example exemplar_idiom)
set_target_properties(exemplar_example PROPERTIES OUTPUT_NAME exemplar_example)
In exemplar.h
#ifndef EXEMPLAR_H_
#define EXEMPLAR_H_
class Exemplar
{
public:
Exemplar() {};
};
#else
#endif // EXEMPLAR_H_
In base.h
#ifndef BASE_H_
#define BASE_H_
#include <string>
class Exemplar;
class Base
{
public:
Base(Exemplar /* no use */);
Base();
virtual Base* make(const std::string& key);
virtual void memberMethod1();
virtual int memberMethod2();
virtual ~Base() {};
protected:
static Base* list_;
Base* next_;
};
#else
#endif // BASE_H_
In base.cpp
#include <string>
#include <iostream>
#include "exemplar.h"
#include "base.h"
Base* Base::list_ = 0;
static Exemplar exemplar;
static Base baseInstance(exemplar);
Base* baseExemplar = &baseInstance;
Base::Base(Exemplar /* no use */) : next_(list_)
{
std::cout << "Base object exemplar ctor" << std::endl;
list_ = this;
}
Base::Base() : next_(0) {}
Base* Base::make(const std::string& key)
{
Base* retval = 0;
for (Base* a = list_; a; a = a->next_)
{
if (a != baseExemplar)
{
if ((retval = a->make(key)))
{
break;
}
}
else if (key == "base")
{
retval = new Base();
break;
}
}
return retval;
}
void Base::memberMethod1()
{
std::cout << "base class memberMethod1() called." << std::endl;
}
int Base::memberMethod2()
{
std::cout << "base class memberMethod2() called." << std::endl;
return 0;
}
In derived1.h
#include "base.h"
#ifndef DERIVED1_H_
#define DERIVED1_H_
class Exemplar;
class Derived1 : public Base
{
public:
Derived1(Exemplar /* no use */);
// Conventional default constructor which will be used to instantiate normal object
Derived1();
virtual Derived1* make(const std::string& key);
virtual void memberMethod1();
virtual int memberMethod2();
};
#else
#endif // DERIVED1_H_
In derived1.cpp
#include <iostream>
#include "exemplar.h"
#include "derived1.h"
static Exemplar exemplar;
static Derived1 derived1Exemplar(exemplar);
Derived1::Derived1(Exemplar a) : Base(a)
{
std::cout << "Derived object exemplar ctor" << std::endl;
}
Derived1::Derived1() : Base() {}
Derived1* Derived1::make(const std::string& key)
{
Derived1* retval = 0;
if (key == "derived1")
{
retval = new Derived1();
}
return retval;
}
void Derived1::memberMethod1()
{
std::cout << "Derived1::memberMethod1" << std::endl;
}
int Derived1::memberMethod2()
{
/* dummy */
std::cout << "Derived1::memberMethod2" << std::endl;
return 0;
}
In main.cpp
#include "base.h"
extern Base* baseExemplar;
int main()
{
Base *ptr = baseExemplar->make("derived1");
if (ptr) {
ptr->memberMethod1();
ptr->memberMethod2();
}
Base *ptr2 = baseExemplar->make("base");
if (ptr2) {
ptr2->memberMethod1();
ptr2->memberMethod2();
}
return 0;
}
When we change the CMakeLists.txt slightly to create shared object by substituting the keyword of 'STATIC' as 'SHARED', then I could see the behavior that I expected. I could see that as a result of static object creation of Derived1 class object (with Exemplar arg), the link list chain in the Base class is properly maintained. And after that, when invoking the virtual make() method from the base exemplar object in main(), I could see both Derived1 object and Base object were successfully created by specifying key string of "derived1" and "base".
However, if I revert back the keyword 'SHARED' to 'STATIC', then I could see the different result. It seems that such static object (Derived1 exemplar object) is not created at all. So it failed to create the Derived1 object by invoking make("derived1") member method.
Is there any way to avoid this issue, even in the case of static library? I would like to expect static object constructor's side-effect towards Base class' link list.
I'm not exactly sure of whether what I'm trying to make here is making sense to you. However, it would be greatly appreciated if you could give me the pointer on the above.
Upvotes: 0
Views: 385
Reputation: 206518
Yes, the GNU linker ld
only adds those symbols that it thinks are actually used. ld
can only check if the symbol is in use, but not if the side effects of the symbol's initialization is used.
Thus, the outcome will be a situation like yours, failures due to non initialization of an unused static object.
You can use --whole-archive
when linking to tell the linker to include all symbols. The drawback is that symbols that you never want to use will also get added and result in increased code size.
Also another thing to note is:
The linker drops unused object files only if they are from a library. Object files explicitly passed to the linker will be always linked.
Upvotes: 1