4

I have the following working code:

class person
{
private:
    int age_;
public:
    person() : age_(56) {}
    void age(int a) { age_ = i; }
}

template < class T, void (T::* ...FUNC)(int) > class holder;

template < class T, void (T::*FUNC)(int)>
class holder<T, FUNC>
{
public:
    typedef typename T::value_type value_type;
public:
    explicit holder() : setter(FUNC) { std::cout << "func\n"; } 
private:
    std::function<void (value_type&, int)> setter;
};

template < class T>
class holder<T>
{
public:
    explicit holder() { std::cout << "plain\n"; }
};

int main()
{
    holder<person> h1;
    holder<person, &person::age> h2;

    // this does not work:
    holder<int> h3;
}

I know that in case of int (or any other non class, struct or union type) the code does not work because of the expect member function in the second template argument.

My question is how to change the code to make it work. I need it work that way, to make the use of my holder class simple.

I've tried it with type traits and also moved the member function pointer to the constructor of the class. Without success.

Any suggestions? Thanks in advance!

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
zussel
  • 157
  • 9
  • Traits are the way to go - what did not work with them? – Björn Pollex Dec 05 '12 at 09:29
  • For testing a tried to make an int specialization with enable_if, but the compiler always mentioned the missing member function from the first holder definition. – zussel Dec 05 '12 at 09:34

1 Answers1

3

Update: I got it to work with std::conditional:

template < class T, void (std::conditional<std::is_class<T>::value, T, struct dummy>::type::* ...FUNC)(int) > class holder;

Another possible solution is to use a subclass:

template < class T, void (T::*FUNC)(int) >
class class_holder
{
public:
    typedef typename T::value_type value_type;
public:
    explicit class_holder() : setter(FUNC) { std::cout << "func\n"; } 
protected:
    std::function<void (value_type&, int)> setter;
}

template <class T, bool IsClass = std::is_class<T>::value>
class holder;

template <class T>
class holder<T, true> : public class_holder<T>
{
public:
    template <void (T::*FUNC)(int) >
    class with_member : public class_holder<T, FUNC>
    {
    };
};

template <class T>
class holder<T, false>
{
public:
    explicit holder() { std::cout << "plain\n"; }
};

int main()
{
    holder<person> h1;
    holder<person>::with_member<&person::age> h2;
    holder<int> h3;
}

I haven't compiled this so tell me if something doesn't work.

Pubby
  • 51,882
  • 13
  • 139
  • 180
  • The conditional way works great for me! Thanks a lot. The haven't checked the sub class version because with conditional it worked the way I want it. – zussel Dec 05 '12 at 09:55
  • @zussel Glad it worked! You can click the checkmark on the left to mark it as the solution. – Pubby Dec 05 '12 at 10:00
  • +1 I'll up vote it just for the creativity =P Btw, how is the T::value_type supposed to resolve. person has no such trait that i see. Where is it coming from? – WhozCraig Dec 05 '12 at 10:11
  • @WhozCraig: That's a remaining of a templated pointer class I put around person (like shared_ptr). The pointer class has a typedef value_type. It isn't used in this example. I adjusted the solution to make it work with my pointer class. – zussel Dec 05 '12 at 11:24
  • @zussel That 'splains it Thanks. And yeah, the conditional expansion is pretty sweet. Took me staring at it for 10-min before I realized how it even worked. – WhozCraig Dec 05 '12 at 11:26
  • @WhozCraig: It took me also a couple of minutes to see that the key is the use of the dummy struct. Pretty clever! – zussel Dec 05 '12 at 11:40
  • @Pubby what do the three dots in ```struct dummy>::type::* ...FUNC``` signify? – user3882729 Jan 14 '22 at 03:51