Set member to same value in all instances of templated structure

1
2
3
4
5
6
7
template <typename T> struct avl_tree {

	T data;
	int balance;
	struct avl_tree <T> *Link[2];
	static int (*comp)(T, T);
};


In main, I have a function like so:
1
2
3
int compare(int a, int b) {
	return ( a - b );
}


Now how do I assign the function pointer in my avl_tree class to the compare function?

I did this:
int (avl_tree<int>::*comp)(int, int) = compare;


But I got the compiler error:
Tree_Test.cc: In function ‘int main()’:
Tree_Test.cc:27:42: error: cannot convert ‘int (*)(int, int)’ to 
‘int (avl_tree<int>::*)(int, int)’ in initialization
 
avl_tree<int>::comp = &compare;
@Disch, I've tried it that way before, but I did it again and this is the compiler error I get:

/tmp/ccfOM2bP.o: In function `main':
/home/chigozie/Desktop/Trees/Tree_Test.cc:27: undefined reference to `avl_tree<int>::comp'
That's a linker error... which is something else entirely.

As with any static member, you need to instantiate it. I forget exactly how to do this with templates, so forgive me if I'm wrong here.... (below is what I think would be the 'obvious' way to do it... but you never know with templates...)

1
2
3
4
5
6
7
8
9
10
11
template <typename T> struct avl_tree {

	T data;
	int balance;
	struct avl_tree <T> *Link[2];
	static int (*comp)(T, T);
};

// You need to add this line to instantiate your static member:
template <typename T>
int (avl_tree<T>::*comp)(T,T);
I put that in the file and now the error is this:

error: template declaration of ‘int (avl_tree<T>::* comp)(T, T)’
What does it mean?

What I am trying to do is to have one comparison function which all instances of avl_tree with the same template type can use. So by making it static, it will reside in memory until the program has finished. However I am trying to give the user the ability to specify their own comparison function so that the code is reuseable.

Is there an easier way to do what I am trying to do? Or is what I am doing the only way to do it?
Last edited on
What does it mean?


It means I screwed up the instantiation code. I'd have to look up an old example of how to do it. I hardly ever do this so I apparently don't have it memorized.


Is there an easier way to do what I am trying to do?


You could do what STL does and pass the comparison function as a template param.
You could do what STL does and pass the comparison function as a template param.

Actually, that's what I've been doing, but I thought this way of doing it would have been cleaner. Guess not lol

Anyways, I found this:
http://stackoverflow.com/questions/9635542/templated-static-member-function-pointer-initialization

Will take a look at it in a bit. Thanks Disch
Last edited on
You just had the * in the wrong place

1
2
3
// You need to add this line to instantiate your static member:
template <typename T>
int (*avl_tree<T>::comp)(T,T);


Andy

@smac

The stackoverflow.com post is talking about a static member which is a member function pointer, rather than a normal function pointer.
Last edited on
@andywestken and @Disch

Thanks for that! appreciate
Is there a way to encapsulate this? I.e. the user only has to call a static method and pass in the function name and the static method will do the rest?

Example:
avl_tree<int>::setcomp(compare);
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
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
template < typename T, typename M > struct cmp_mem_var_t
{
    explicit cmp_mem_var_t( M T::*mv ) : mv(mv) {}

    bool operator() ( const T& first, const T& second ) const
    { return first.*mv < second.*mv ; }

    private: M T::*mv ;
};

template < typename T, typename M > // syntactic sugar
cmp_mem_var_t<T,M> compare( M T::*mv ) { return cmp_mem_var_t<T,M>(mv) ; }

template < typename T, typename R > struct cmp_mem_fun_t
{

    explicit cmp_mem_fun_t( R (T::*mfn)() const ) : mfn(mfn) {}

    bool operator() ( const T& first, const T& second ) const
    { return (first.*mfn)() < (second.*mfn)() ; }

    private: R (T::*mfn)() const ;
};

template < typename T, typename R > // syntactic sugar
cmp_mem_fun_t<T,R> compare( R (T::*mfn)() const ) { return cmp_mem_fun_t<T,R>(mfn) ; }

struct A { int n ; double v ; double foo() const { return n+v ; } };

#include <string>
#include <cctype>

struct B
{
    std::string s ;
    std::string bar() const
    {
        std::string t = s ;
        for( char& c : t ) c = std::toupper(c) ;
        return t ;
    }
};

#include <algorithm>
#include <iterator>
#include <iostream>

int main() // simple test driver
{
    A seq_a[] = { { 3, 3.3 }, { 5, 2.2 }, { 2, 6.6 }, { 7, 5.5 }, { 6, 1.1 } } ;
    const auto print_a = [&seq_a] ()
    {
        for( const A& a : seq_a ) std::cout << "A{ " << a.n << ", " << a.v << " }  " ;
        std::cout << '\n' ;
    };
    print_a() ;

    std::sort( std::begin(seq_a), std::end(seq_a), compare(&A::n) ) ;
    print_a() ;

    std::sort( std::begin(seq_a), std::end(seq_a), compare(&A::v) ) ;
    print_a() ;

    std::sort( std::begin(seq_a), std::end(seq_a), compare(&A::foo) ) ;
    print_a() ;

    std::cout << '\n' ;

    B seq_b[] = { { "mno" }, { "ghi" }, { "abc" }, { "JKL" }, { "DEF" } } ;
    const auto print_b = [&seq_b] ()
    {
        for( const B& b : seq_b ) std::cout << "B{ '" << b.s << "' }  " ;
        std::cout << '\n' ;
    };
    print_b() ;

    std::sort( std::begin(seq_b), std::end(seq_b), compare(&B::s) ) ;
    print_b() ;

    std::sort( std::begin(seq_b), std::end(seq_b), compare(&B::bar) ) ;
    print_b() ;
}

http://ideone.com/PGEhS5

Note: with bind expressions and call wrappers, the member function version can be generalized to handle member functions with any arity.
JLBorges, please explain. What is line 51 and 70 doing?

And I thought the variable has to be static for you to use the scope operator, i.e. line 58, 61, 64, 77, 80.

In general what is the code doing especially the part before line 30?
> What is line 51 and 70 doing?

These involve lambda expressions (or closures).
print_a holds the result of evaluating the lambda expression; it is a nullary callable object of an unspecified (implementation-dependant) type.
See: http://www.cprogramming.com/c++11/c++11-lambda-closures.html



> And I thought the variable has to be static for you to use the scope operator, i.e. line 58, 61, 64, 77, 80.

The snippet uses pointers to (non-static) members.

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 <memory>

struct A
{
    int i = 5 ;
    int j = 10 ;
    int foo() const { return i+j ; }
};

int main()
{
    int A::*pim = nullptr ; // type of pim is 'pointer to int member of A'
    pim = &A::i ; // pim now points to A::i
    pim = &A::j ; // pim now points to A::j

    // *pim = 8 ; // *** error: pim is not of type 'pointer to int'
    // to dereferece pim, we need an object of static type A
    // for pointers to members, the dereference operator is a binary operator
    A a ;
    ( a.*pim ) = 8 ; // fine
    A* pa = std::addressof(a) ;
    ( pa->*pim ) = 9 ; // fine

    // type of pmf is 'pointer to non-static member funcytion of A
    // which returns a prvalue of type int, takes no explicit arguments
    // and is const-qualified'
    int (A::*pmf)() const = nullptr ;

    pmf = &A::foo ; // pmf now points to A::foo

    // to dereferece pmf, we need an object of static type A
    int result = ( a.*pmf )() ; // fine
    result = ( pa->*pim ) = 9 ; // fine
}

See: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=142
http://www.parashift.com/c++-faq/pointers-to-members.html



> In general what is the code doing especially the part before line 30?

cmp_mem_var_t and cmp_mem_fun_t are binary predicates implemented as function objects which internally use pointers to members.

Once you have understood how pointers to members are used, you should be able to understand how these function objects work.
Last edited on
Topic archived. No new replies allowed.