0

I need to specialize

template< typename T, int Id >
struct ValueTraits
{
  // Default version
  static int getValue() { return 0; }
};

for any subclass of some ConcreteClass as T:

class ConcreteClass {};

struct ConcreteSub1: public ConcreteClass
{
  static int get() { return 1; }
};

struct ConcreteSub2: public ConcreteClass
{
  static int get() { return 2; }
};

and for some predetermined value of Id, say 123,

...so that ValueTraits< ConcreteSub1, 123 >::getValue() would call ConcreteSub1::get() and return 1, and ValueTraits< ConcreteSub2, 123 >::getValue() would call ConcreteSub2::get() and return 2. Use of any Id other than 123 or the class other than a subclass of ConcreteClass should fall back to the default version of the template.

Now, I understand that I could use std::enable_if with std::is_base_of, but wouldn't that require adding an additional dummy type argument to ValueTraits? Something like this would work, for example:

#include <stdio.h>
#include <type_traits>

template< typename T, int Id, typename Dummy = void >
struct ValueTraits
{
  // Default version
  static int getValue() { return 0; }
};

class ConcreteClass {};

struct ConcreteSub1: public ConcreteClass
{
  static int get() { return 1; }
};

struct ConcreteSub2: public ConcreteClass
{
  static int get() { return 2; }
};

template< typename T >
struct ValueTraits< T, 123, typename std::enable_if<
  std::is_base_of< ConcreteClass, T >::value >::type >
{
  static int getValue() { return T::get(); }
};

int main()
{
  // prints 1, 2, 0
  printf( "%d, %d, %d\n",
          ValueTraits< ConcreteSub1, 123 >::getValue(),
          ValueTraits< ConcreteSub2, 123 >::getValue(),
          ValueTraits< int, 123 >::getValue() );
  return 0;
}

The problem is, I can't add an additional dummy type argument to ValueTraits, since it's part of a library which actually provides this ValueTraits for me to specialize.

So my question is, can I perform this specialization with the original version of ValueTraits taking only typename and int, and how?

EDIT: To clarify, I am not the user of ValueTraits either - the library provides it for me to specialize, and then the library again instantiates it with my types to get the behavior I've defined in my specializations. So I have no control on neither the definition of ValueTraits nor on the way it's used afterwards.

dragonroot
  • 5,653
  • 3
  • 38
  • 63

2 Answers2

1

GCC 4.8+ is OK with this, but not Clang:

template< typename T >
struct ValueTraits< T, std::enable_if<std::is_base_of< ConcreteClass, T >::value,
                                      std::integral_constant<int, 123> >::type::value >
{
  static int getValue() { return T::get(); }
};

I'm inclined to think that Clang is currently right, but CWG 1315 is likely to change the rules here so that the above code becomes valid, so you are probably safe if you only need this to work on GCC 4.8 or later.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • This is quite brilliant, using `integral_constant` as a type for `enable_if`, and then accessing its value... Yeah, I have a feeling specifying a non-type argument like that violates the standard, since it involves a template parameter T. It might be a gcc bug, which would make it risky to depend on the current behavior. Still, I really like this `integral_constant` trick. – dragonroot Feb 18 '16 at 04:25
  • @dragonroot Like I said, CWG 1315 should (hopefully soon) make this officially (TM) valid code. I can't think of anything better at the moment. – T.C. Feb 18 '16 at 09:53
  • @dragonroot Most certainly part of C++17 (C++11 is long ratified). I anticipate it being resolved in Rapperswil (Beginning of March). – Columbo Feb 19 '16 at 00:27
  • @Columbo You mean Jacksonville? And compilers usually implement DRs retroactively anyway. (Also, nice paper.) – T.C. Feb 19 '16 at 00:31
  • @T.C. Sorry; Jacksonville. :) Concerning DRs: Surely they aren't resolved retroactively, though? Or is that irrelevant? – Columbo Feb 19 '16 at 00:39
  • @Columbo Well, since nobody is going to implement C++11 exactly as published, does it really matter? (My current favorite example: nobody is going to parse the `` in `vector` as a *header-name*, max-munch notwithstanding.) – T.C. Feb 20 '16 at 02:12
  • @T.C. Issue 2000. Very nice :-) – Columbo Feb 20 '16 at 12:06
0

Interesting question , but it would helped more if you provided some additional use cases or more info, because the way it's going to be used can change details of implementation.

Ok, now back to the answer, so considering that there is a library there that you can't modify here is something that you can do:

First have a class with in any shape or form you want:

template< typename T, typename Dummy>
struct MyOwnSpecializedClassInAnyWayIWant
{
    // your own specialization:
    static int getValue() { return 0; }
};

Now you only need to specialize ValueTraits once:

template< typename T , typename Dummy = void >
struct ValueTraits < MyOwnSpecializedClassInAnyWayIWant<T, Dummy> >
{
    // Default version
    static int getValue() { return MyOwnSpecializedClassInAnyWayIWant<T, Dummy>::getValue(); }
};

And the use case would be something like:

ValueTraits< MyOwnSpecializedClassInAnyWayIWant< ConcreteSub1 >>::getValue()

Now this is kind of a pain to write like that so you can make a using:

template <class T>
using ValueTraits_t = ValueTraits< MyOwnSpecializedClassInAnyWayIWant< T > >;
//use ValueTraits_t<ConcreteSub1>;

As a side note i removed the Id for simplicity but this can work with that also.

Raxvan
  • 6,257
  • 2
  • 25
  • 46
  • I've edited the question to clarify this. ValueTraits is instantiated by the library too, so I have no control on how it gets to be instantiated. I'm only supposed to provide specializations for it, and so far I can only figure how to do the simpler ones. – dragonroot Feb 17 '16 at 20:24
  • @dragonroot i think an approach like this could still work even is the library is the user of `ValueTraits`. I don't have any other solution for this problem so i'm just going to leave my reply here for the record. – Raxvan Feb 18 '16 at 08:53