Undefined reference errors

Apr 15, 2009 at 9:50pm
Hi,

I am unable to build the following program but do not know why.

I have a header SmartEnum.h which I am including in my test program Smart_Enum.h
I am using CodeBlocks and adding the directory where SmartEnum.h exists in the search path and even if I hard code the include I still get the undefined reference errors, to be precise undefined reference to `Enum<Test_Enum>::s_instances' in several places or to be exact anywhere in the SmartEnum.h file that references s_instances.

I am using CodeBlocks as my ide version SVN 5535
and gcc (4.3.3-tdm-1 mingw32) 4.3.3

Here is a listing of the four files

SmartEnum.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <functional> // needed by using_iterators()
#include <iterator> // needed by using_iterators()
#include <algorithm> // needed by using_iterators()
#include <cctype> // needed by using_iterators()

#include <set>
template <class T>
class Enum
{
private:

    // Predicate for finding the corresponding instance
    struct Enum_Predicate_Corresponds:
                public std::unary_function<const Enum<T>*, bool>
    {
        Enum_Predicate_Corresponds(int Value): m_value(Value) { }
        bool operator()(const Enum<T>* E)
        {
            return E->Get_Value() == m_value;
        }
private:
        const int m_value;
    };
    // Comparison functor for the set of instances
    struct Enum_Ptr_Less:
                public std::binary_function<const Enum<T>*, const Enum<T>*, bool>
    {
        bool operator()(const Enum<T>* E_1, const Enum<T>* E_2)
        {
            return E_1->Get_Value() < E_2->Get_Value();
        }
    };

public:
    // Compiler-generated copy constructor and operator= are OK.
    typedef typename std::set<const Enum<T>*, Enum_Ptr_Less> instances_list;
    typedef typename instances_list::const_iterator const_iterator;

protected:
    // Constructors
    explicit Enum(int Value):m_value(Value)
    {
        s_instances.insert(this);
    };
public:
    // Access to int value
    int Get_Value(void) const
    {
        return m_value;
    }
    static int Min(void)
    {
        return (*s_instances.begin())->m_value;
    }
    static int Max(void)
    {
        return (*s_instances.rbegin())->m_value;
    }
    static const Enum<T>* Corresponding_Enum(int Value)
    {
        const_iterator it = std::find_if(s_instances.begin(), s_instances.end(),
                                         Enum_Predicate_Corresponds(Value));
        return (it != s_instances.end()) ? *it : NULL;
    }
    static bool Is_Valid_Value(int Value)
    {
        return Corresponding_Enum(Value) != NULL;
    }
    // Number of elements
    static int size(void)
    {
        return s_instances.size();
    }
    // Iteration
    static const_iterator begin(void)
    {
        return s_instances.begin();
    }
    static const_iterator end(void)
    {
        return s_instances.end();
    }

private:
    int m_value;
    static instances_list s_instances;
};

Smart_Enum.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "..\Smart_Enum\SmartEnum.h"

class Test_Enum: public Enum<Test_Enum>
{

private:
    explicit Test_Enum(int Value): Enum<Test_Enum>(Value) { }

public:
    static const Test_Enum enum_11;
    static const Test_Enum enum_12;
    static const Test_Enum enum_18;
    static const Test_Enum enum_20;
    static const Test_Enum enum_21;
};

Smart_Enum.cpp
1
2
3
4
5
6
7
8
9
#include "Smart_Enum.h"

template<> Enum<Test_Enum>::instances_list Enum<Test_Enum>::s_instances;

const Test_Enum Test_Enum::enum_11(11);
const Test_Enum Test_Enum::enum_12(12);
const Test_Enum Test_Enum::enum_18(18);
const Test_Enum Test_Enum::enum_20(20);
const Test_Enum Test_Enum::enum_21(21);

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "Smart_Enum.h"

#include<iostream>
int main()
{
    using std::cout;
    using std::endl;
    int Cur_Elem = 0;
    for ( Test_Enum::const_iterator i = Test_Enum::begin();
            i != Test_Enum::end(); ++i )
    {
        Cur_Elem++;
        cout << "Test_Enum element #" << Cur_Elem << " value = "
             << (*i)->Get_Value() << endl;
    }

    cout << "Total #elements = " << Test_Enum::size() << endl;
    cout << "Min enum value = " << Test_Enum::Min() << endl;
    cout << "Max enum value = " << Test_Enum::Max() << endl;

    for ( int i = Test_Enum::Min(); i <= Test_Enum::Max(); ++i )
    {
        cout << i;
        if ( Test_Enum::Is_Valid_Value(i) ) cout << " is ";
        else cout << " isn't ";
        cout << "a valid value for Test_Enum." << endl;
    }

    Test_Enum E(Test_Enum::enum_11);
    cout << "Value of E = " << E.Get_Value() << endl;

    E = Test_Enum::enum_20;
    cout << "Value of E = " << E.Get_Value() << endl;

    // Illegal code
    // bool B = E; // won't compile, illegal implicit conversion
    // E++; // won't compile, cannot treat Test_Enum as an int
    // Test_Enum X(17); // won't compile, ctor is private

    return 0;
}


This code came originally from this link http://www.ddj.com/cpp/184403955?pgno=1
And I have already had to google to get this far.

As far as I know undefined reference means the linker cant find something, but the file is there and the problem seems to be in the file I am including.

I am stuck and google only seems to point in the linker cant find something problem, even though this seems to be a problem in the file that is included rather than not being able to find the file itself.

Any help will be very much appreciated
Confused.


Last edited on Apr 15, 2009 at 10:20pm
Apr 16, 2009 at 12:09pm
Nowhere is

 
 static instances_list s_instances;


actually instantiated somewhere; this only declares the variable.

It has to be instantiated in the same manner as the static consts in Smart_Enum.{h,cpp}
Apr 16, 2009 at 8:49pm
I am not sure I understand?
SmartEnum.h is just a header file for use with the Smart_Enum files that inherit from Enum.

This builds and compiles without error or warning in Visual C++ 2008.

Apr 16, 2009 at 10:56pm
You don't understand.
Suppose you have
1
2
3
struct A{
    static int a;
};

If you leave that as it is, the linker will produce an error because A::a is never defined (or instantiated, as jsmith put it). Only declared.
To define A::a, this has to appear somewhere, preferably only once in the entire compilation:
int A::a=0;
Apr 17, 2009 at 6:04pm
I understand, but how come this links and compiles without error or warning (all warnings enabled) in Visual C++ 2008 and not g++?

This does actually compile if I have all the files in a single build project, but I really want the SmartEnum. as just an include header for use in other projects.

This class is only supposed to be inherited from afaics.

Ok, I created a SmartEnum.cpp class and added template <> Enum<int>::Instances_list Enum<int>::s_instances and this compiled successfully into a libray file.

However in the Smart_Enum project when I add the library to the project and include the .h file I still get the same errors any ideas?

Got this working eventually with a lot of faffing about.

In the end made the following changes to SmartEnum.h
changed s_instances to be a pointer

added this to the constructor
if (!s_instances)
{
s_instances = new instances_list;
}

This change to SmartEnum.cpp
template<class T>
typename Enum<T>::instances_list *Enum<T>::s_instances = 0;

This to Smart_Enum.cpp
template <class T> typename Enum<T>::instances_list *Enum<T>::s_instances = 0;

I found this info from this post

http://webui.sourcelabs.com/gcc/mail/user/threads/Initialization_of_a_static_member_of_a_template_class_fails.meta

The main thing is that it works :)

I have SmartEnum and Smart_Enum built as a libraries
and main referencing LibSmart_Enum_Test.a by explicitly adding this library to the linker and only using the header file.

This was a good learning experience for me and I have gained some valuable knowledge that different compilers deal with templates in different ways, IMHO I think Visual C++ provides the best implementation by far.

Thanks for your help
Not so confused
Last edited on Apr 18, 2009 at 6:58pm
Topic archived. No new replies allowed.