Smg
Smg

Reputation: 1143

Static object that is not referenced will not be created in case of static library?

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

Answers (1)

Alok Save
Alok Save

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

Related Questions