1

While trying to apply policy-based design, I got stuck on this one (simplified):

template <class TPrintPolicy, typename T>
struct A : private TPrintPolicy {
    using TPrintPolicy::Print;
    T t;
    void Foo() {
        Print(t);
    }
};

struct IntPolicy {
    void Print(int n) {
        std::cout << n << std::endl;
    }
};

int main(int argc, char* argv[]) {
    A<IntPolicy, int> a;
    a.Foo();
    return 0;
}

And here's the question: How should I redefine class A so that it would be possible to provide only the policy parameter to the A template, to let it infer T on its own, like this:

A<IntPolicy> a;

Preferrably, policy definition should not be much more complicated then it is now. Any ideas?

EDIT:
I forgot to mention that I do not want a policy to export a typedef. That is of course the easy solution, but cannot A infer the type of T on its own?

dnlgl
  • 33
  • 5
  • Unrelated: `template ` is just a legacy syntax for `template ` and there is no difference between them (and typename is preferred). – Cat Plus Plus Feb 09 '11 at 19:47
  • In very specific case, there is a difference between class and typename, see [this](http://stackoverflow.com/questions/2023977/c-difference-of-keywords-typename-and-class-in-templates) – Errata Feb 09 '11 at 20:04

3 Answers3

5

Make a typedef within each policy that gives the policy's data type.

template <class TPrintPolicy>
struct A : private TPrintPolicy {
    using TPrintPolicy::Print;
    typename TPrintPolicy::data_type t;
    void Foo() {
        Print(t);
    }
};

struct IntPolicy {
    typedef int data_type;
    void Print(int n) {
        std::cout << n << std::endl;
    }
};

int main(int argc, char* argv[]) {
    A<IntPolicy> a;
    a.Foo();
    return 0;
}

Edit: Personally, I would probably use a typedef, even though it adds some duplication, because I believe it makes the code much clearer. However, if you really want to avoid it, you could try something similar to this.

#include <boost/typeof/typeof.hpp>

template<typename T, typename Class>
T DeduceArgumentType(void (Class::*ptr)(T)) {}

template <class TPrintPolicy>
struct A : private TPrintPolicy {
    using TPrintPolicy::Print;
    typedef BOOST_TYPEOF(DeduceArgumentType(&TPrintPolicy::Print)) data_type;
    data_type t;
    void Foo() {
        Print(t);
    }
};

struct IntPolicy {
    void Print(int n) {
        std::cout << n << std::endl;
    }
};

int main(int argc, char* argv[]) {
    A<IntPolicy> a;
    a.Foo();
    return 0;
}

It might also be possible to get Boost.TypeTraits to do this, but I'm not sure how.

Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
  • Oh, that was quick, but unfortunately I forgot to mention that I do not want to go this way. My wish is not to mention the type of T explicitly more than once (in the declaration of Print()). Sorry, and have an upvote! – dnlgl Feb 09 '11 at 19:50
  • @dnlgl, use the typedef in the `Print` method then, or alternatively, template the `IntPolicy`, something like: `template struct Policy { typedef DataType data_type; void Print(DataType n) {...} };`, then in `main`, `A > a;` – Nim Feb 09 '11 at 20:05
  • @Nim, I have thought about templating the policy, but that felt like cheating :-) I really wonder if there is any solution to this without an extra mention of the explicit type of T? – dnlgl Feb 09 '11 at 20:13
  • I get: 'T DeduceArgumentType(void (__thiscall Class::* )(T))' : could not deduce template argument for 'void (__thiscall Class::* )(T)' from 'A::TPrintPolicy::Print' – dnlgl Feb 09 '11 at 20:22
  • @dnlgl: Which compiler? It works in g++ version 4.3.2 and Clang 2.8. If your compiler supports it, you might try C++0x's decltype instead of BOOST_TYPEOF. – Josh Kelley Feb 09 '11 at 20:33
  • VC 2010. Unfortunately I don't want to move to C++0x yet. And I want it to be portable. – dnlgl Feb 09 '11 at 20:37
1

I guess there are a few ways to do it, but without knowing where t comes from it's hard to give a clear answer. If T is always dependent on the type passed to the template, one way is to define a typedef to the desired type in IntPolicy, and use that to define your printed type in A.

template <class TPrintPolicy>
struct A : private TPrintPolicy {
    using TPrintPolicy::Print;

    typename TPrintPolicy::print_type t;
    void Foo() {
        Print(t);
    }
};

struct IntPolicy {

    typedef int print_type;
    void Print(int n) {
        std::cout << n << std::endl;
    }
};

int main(int argc, char* argv[]) {
    A<IntPolicy> a;
    a.t = 45;
    a.Foo();
    return 0;
}

There are probably more robust ways to do this, but again, depends on your overall design.

gr0v3r
  • 698
  • 5
  • 5
1

I prefer Josh'es answer, but if Policy possible argument types is known you can do something like this. I've used SFINAE and type lists from Loki library.

template<class Type, class Policy>
struct CheckPrintMethod{
   typedef struct { char table[2]; } yes;
   typedef char no;

   template<typename U, void (U::*)(Type)> struct SFINAE {};

   template<class T> static yes func( SFINAE<T, &T::Print> *);
   template<class T> static no func(...);

   static const bool value = (sizeof(func<Policy>(0)) == sizeof(yes));
};

template<class H, class T>
struct List{
  typedef H Head;
  typedef T Tail;
};

class NullType{};

template<bool, class Y, class N>
struct ifelse{
  typedef N type;
};

template<class Y, class N>
struct ifelse<true, Y, N>{
  typedef Y type;
};

template<class TypeList, class Policy>
struct CheckReturnType{
  typedef typename ifelse<
            CheckPrintMethod<typename TypeList::Head, Policy>::value, 
            typename TypeList::Head, 
            typename CheckReturnType<typename TypeList::Tail, Policy>::return_type
          >::type return_type;
};

template<class Policy>
struct CheckReturnType<NullType, Policy>{
  typedef NullType return_type;
};

template <class TPrintPolicy>
struct A : private TPrintPolicy {
    using TPrintPolicy::Print;
    typedef List<int, List<char, List<string, NullType> > > PossibleTypesList;
    typedef typename CheckReturnType<PossibleTypesList, TPrintPolicy >::return_type print_type;
    print_type t;
    A(print_type obj): t(obj) {}
    void Foo() {
        Print(t);
    }
};

struct IntPolicy {
    void Print(int n) {
        std::cout << "Int: " << n << std::endl;
    }
};

struct StringPolicy {
    void Print(string str) {
        std::cout << "String: " << str << endl;
    }
};

int main(int argc, char* argv[]) {
    A< IntPolicy> a(3);
    a.Foo();
    A< StringPolicy > b("test");
    b.Foo();
    return 0;
}
Pawel Zubrycki
  • 2,703
  • 17
  • 26
  • This direction seems promising, but unfortunately currently it requires mentioning the explicit type in the type list, which is one extra mention I would like to avoid. Could be helpful though in the case where there are many policies with few types. – dnlgl Feb 10 '11 at 06:40