One unique static variable for each class, in otherwise identical classes

There are several classes that are identical, except for one static variable which must be unique for each class.
What is the C++ syntax to set that up?

The following example is my failed attempt using template class.
Code_LayerLock is a template class.
The Code_LayerLock classes are identical, except for the static refLayerState variable.
Here is the tricky part: I want each unique Code_LayerLock class that the compiler generates to have it's own static refLayerState variable.
But when the example is compiled, the compiler finds this error on line 32:
 
    template_ref.cpp:32:54: error: redefinition of ‘State& Code_LayerLock<State>::refLayerState’
The error happens because class Code_LayerLock is still the same name, even though the compiler generated a unique class for the template parameter declaration.

Maybe template class is not the best approach in this situation.
How would you assign a unique static variable to each class, to otherwise identical classes?

I will be using GCC: 4.8.1, std=c++98 for Arduino.

Thank you.

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
#include <iostream>

class LayerStateMF
{
    public:
        void lock(int layer) { std::cout << "LayerStateMF::layer=" << layer << std::endl; }
};

class Code_StateFunc
{
    public:
        void lock(int layer) { std::cout << "Code_StateFunc::layer=" << layer << std::endl; }
};

template <class State>
class Code_LayerLock
{
    private:
        const int layer;
        static State& refLayerState;        //refLayerState is static and different for each class
    public:
        Code_LayerLock(const int layer) : layer(layer) {}
        void press() { refLayerState.lock(layer); }
};

LayerStateMF MF;
Code_LayerLock<LayerStateMF> l_MF(0);
template<class State> State& Code_LayerLock<State>::refLayerState = MF; //static variable definition

Code_StateFunc l_NASHold;
Code_LayerLock<Code_StateFunc> l_TenKey(1);
template<class State> State& Code_LayerLock<State>::refLayerState = l_NASHold; //error: redefinition

int main()
{
    l_MF.press();
    //l_TenKey.press();
}

Expected output:
1
2
LayerStateMF::layer=0
Code_StateFunc::layer=1
Last edited on
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
#include <iostream>

class LayerStateMF
{
    public:
        void lock(int layer) { std::cout << "LayerStateMF::layer=" << layer << std::endl; }
};

class Code_StateFunc
{
    public:
        void lock(int layer) { std::cout << "Code_StateFunc::layer=" << layer << std::endl; }
};

template <class State>
class Code_LayerLock
{
    private:
        const int layer;
        static State layerState;        //refLayerState is static and different for each class
    public:
        Code_LayerLock(const int layer) : layer(layer) {}
        void press() { layerState.lock(layer); }
};

template<> LayerStateMF Code_LayerLock<LayerStateMF>::layerState{}; //static variable definition

template<> Code_StateFunc Code_LayerLock<Code_StateFunc>::layerState{}; //static variable definition

int main()
{
    Code_LayerLock<LayerStateMF> l_MF(0) ;
    l_MF.press();

    Code_LayerLock<Code_StateFunc> l_NASHold(1) ;
    l_NASHold.press();
}

http://coliru.stacked-crooked.com/a/80bd958ea4528048
If most of the State classes are DefaultConstructible (as they are in this example):

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
#include <iostream>

class LayerStateMF
{
    public:
        void lock(int layer) { std::cout << "LayerStateMF::layer=" << layer << std::endl; }
};

class Code_StateFunc
{
    public:
        void lock(int layer) { std::cout << "Code_StateFunc::layer=" << layer << std::endl; }
};

template <class State>
class Code_LayerLock
{
    private:
        const int layer;
        static State layerState;        //refLayerState is static and different for each class
    public:
        Code_LayerLock(const int layer) : layer(layer) {}
        void press() { layerState.lock(layer); }
};

template< typename State > State Code_LayerLock<State>::layerState{} ; //static variable definition

int main()
{
    Code_LayerLock<LayerStateMF> l_MF(0) ;
    l_MF.press();

    Code_LayerLock<Code_StateFunc> l_NASHold(1) ;
    l_NASHold.press();
}

http://coliru.stacked-crooked.com/a/ba3d895fef508db7

WE need to specialise the definition of State Code_LayerLock<>::layerState only for those states that are not DefaultConstructible
Last edited on
Thanks JLBorges !! That works.
Some day I hope to reach your level of template foo.

What are the "{}" braces for at the end of line 26?:
 
template<> LayerStateMF Code_LayerLock<LayerStateMF>::layerState{};

On Arduino and older compilers it's:
 
template<> LayerStateMF Code_LayerLock<LayerStateMF>::layerState = {};
Last edited on
> What are the "{}" braces for at the end of line 26?

Value initialisation: (a named variable is declared with the initializer consisting of a pair of braces.)
http://en.cppreference.com/w/cpp/language/value_initialization
Thanks for the quick response JLBorges. Just one more question.

The way you defined the variable layerState, it looks like a copy by value. I really need it to be a reference or pointer.
The value_initialization link your provided says, "References cannot be value-initialized."
So I changed layerState to a pointer and it works beautifully in c++11, but I need it to work in c++98 for Arduino.
The value initialization on line 26 won't work on c++98. Here is the compiler message:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ g++ template_ref.cpp
template_ref.cpp: In instantiation of ‘LayerStateMF* Code_LayerLock<LayerStateMF>::layerState’:
template_ref.cpp:41:24:   required from ‘void Code_LayerLock<State>::press() [with State = LayerStateMF]’
template_ref.cpp:57:16:   required from here
template_ref.cpp:47:35: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
 template< typename State > State* Code_LayerLock<State>::layerState = {} ; //static variable definition
                                   ^
template_ref.cpp: In instantiation of ‘Code_StateFunc* Code_LayerLock<Code_StateFunc>::layerState’:
template_ref.cpp:41:24:   required from ‘void Code_LayerLock<State>::press() [with State = Code_StateFunc]’
template_ref.cpp:60:21:   required from here
template_ref.cpp:47:35: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11

$ ./a.out
LayerStateMF::layer=0
Code_StateFunc::layer=1

Is there an alternative syntax to the extended initializer list?

Example with initializer list and static pointer State* ptrLayerState:
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
#include <iostream>

class LayerStateMF
{
    public:
        void lock(int layer) { std::cout << "LayerStateMF::layer=" << layer << std::endl; }
};

class Code_StateFunc
{
    public:
        void lock(int layer) { std::cout << "Code_StateFunc::layer=" << layer << std::endl; }
};

template <class State>
class Code_LayerLock
{
    private:
        const int layer;
        static State* ptrLayerState;        //refLayerState is static and different for each class
    public:
        Code_LayerLock(const int layer) : layer(layer) {}
        void press() { ptrLayerState->lock(layer); }
};

template< typename State > State* Code_LayerLock<State>::ptrLayerState = {} ; //static variable definition

int main()
{
    Code_LayerLock<LayerStateMF> l_MF(0) ;
    l_MF.press();

    Code_LayerLock<Code_StateFunc> l_NASHold(1) ;
    l_NASHold.press();
Last edited on
> I really need it to be a reference or pointer.

Why?
Because there are 3 other classes accessing one of the LayerStates.
And pointers would be a way of sharing the LayerState between all the classes.

Since its only one of the LayerStates, I could copy & paste one Code_LayerLock class and add a ptrLayerState to it.
But if the template syntax isn't too awful, it would be better to use a reference or pointer.
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
#include <iostream>

class LayerStateMF
{
    public:
        void lock(int layer) { std::cout << "LayerStateMF::layer=" << layer << std::endl; }
        static LayerStateMF& shared_instance() ;
};

class Code_StateFunc
{
    public:
        void lock(int layer) { std::cout << "Code_StateFunc::layer=" << layer << std::endl; }
        static Code_StateFunc& shared_instance() ;
};

template <class State>
class Code_LayerLock
{
    private:
        const int layer;
        static State& layerState;

    public:
        Code_LayerLock(const int layer) : layer(layer) {}
        void press() { layerState.lock(layer); }
};

template< typename State > State& Code_LayerLock<State>::layerState = State::shared_instance() ;

int main()
{
    Code_LayerLock<LayerStateMF> l_MF(0) ;
    l_MF.press();

    Code_LayerLock<Code_StateFunc> l_NASHold(1) ;
    l_NASHold.press();
}

LayerStateMF& LayerStateMF::shared_instance() { static LayerStateMF object ; return object ; }

Code_StateFunc& Code_StateFunc::shared_instance() { static Code_StateFunc object ; return object ; }

http://coliru.stacked-crooked.com/a/dd99f21bf762c18c
Thanks for your help JLBorges.
That last example blows my mind :)
If there are many states, and most of them are DefaultConstructible, CRTP would be handy.
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

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
#include <iostream>
#include <typeinfo>

template < typename T > struct state_base
{
    static T& shared_instance() ;
    void lock(int layer) { std::cout << typeid(T).name() << "::lock layer=" << layer << '\n' ; }
};

template < typename T > T& state_base<T>::shared_instance() { static T object ; return object ; }

struct LayerStateMF : state_base<LayerStateMF>
{ void lock(int layer) { std::cout << "LayerStateMF::lock layer=" << layer << '\n' ; }  };

struct Code_StateFunc : state_base<Code_StateFunc>
{ void lock(int layer) { std::cout << "Code_StateFunc::lock layer=" << layer << '\n' ; } };

struct state_three : state_base<state_three> {};
struct state_four : state_base<state_four> {};

struct state_five // not DefaultConstructible
{
    state_five(int) {}
    void lock(int layer) { std::cout << "state_five::lock layer=" << layer << '\n' ; }
    static state_five& shared_instance() ;
};

template < typename State > class Code_LayerLock
{
    const int layer;
    static State& layerState;

    public:
        Code_LayerLock(const int layer) : layer(layer) {}
        void press() { layerState.lock(layer); }
};

template< typename State > State& Code_LayerLock<State>::layerState = State::shared_instance() ;

int main()
{
    Code_LayerLock<LayerStateMF> zero(0) ;
    zero.press();

    Code_LayerLock<Code_StateFunc> one(1) ;
    one.press();

    Code_LayerLock<state_three> three(3) ;
    three.press();

    Code_LayerLock<state_four> four(4) ;
    four.press();

    Code_LayerLock<state_five> five(5) ;
    five.press();
}

state_five& state_five::shared_instance() { static state_five object(999) ; return object ; }

http://coliru.stacked-crooked.com/a/beb9119d97e555a5
Topic archived. No new replies allowed.