I'm making a struct Box<T> that handles some data. The specifics are unimportant.
An important note however is that Box<T> can store a pointer, but it might not. So both Box<int> and Box<int *> are valid. Obviously, if we own Box.data, we're going to need to delete data if it is a pointer type.
Here's a solution I came up with that works in C++11:
template <typename T> struct BoxTraits;
template <typename T> struct Box {
using traits_t = BoxTraits<T>;
T data;
~Box() = default; // not required, I know
T get_data() { return traits_t::get_data(this); }
};
template <typename T> struct Box<T *> {
using traits_t = BoxTraits<T *>;
T *data;
~Box() { delete data; }
T *get_data() { return traits_t::get_data(this); }
};
template <typename T> struct BoxTraits {
static T get_data(Box<T> *const box) { return box->data; }
};
Box::get_data is here to illustrate an issue with this design pattern. For every single method I want to add to Box, I need to add some boiler plate in each specialisation. Note that I would also need a Box<T *const> specialisation.
This seems like quite a rubbish solution. In C++14, I could use if constexpr with a is_ptr<T> trait and only have to write extra code in the methods that need specialising... Is there any way I can do this is in C++11?
This solution is shorter, cleaner and works for Box<U *const>!
template <typename T> struct is_ptr { static const bool value = false; };
template <typename U> struct is_ptr<U *> { static const bool value = true; };
template <typename U> struct is_ptr<U *const> {
static const bool value = true;
};
template <typename T> struct Box {
T data;
~Box() {
if constexpr (is_ptr<T>::value) {
delete data;
}
}
T get_data() { return data; }
};
First off, C++11 already has std::is_pointer, no need to roll your own. You can see that it inherits from std::true_type or std::false_type instead of defining its own value member. The reason for that is tag dispatching, that can effectively replace if constexpr in this situation:
template <typename T> struct Box {
T data;
~Box() {
destroy(std::is_pointer<T>{});
}
private:
void destroy(std::true_type) {
delete data;
}
void destroy(std::false_type) {} // nothing to do
};
Demo
I think this is the most idiomatic way in C++11 for delegating to different implementations based on type traits. In many situations, tag dispatching can replace if constexpr (from C++17, not C++14), and I believe the latter always replaces the former in addition to being clearer. Tag dispatching can also be used before C++11 if you roll your own type traits.
Last note: you don't need to use the standard type traits, you can do something like this:
template <typename T> struct is_ptr { static const bool value = false; };
template <typename T> struct is_ptr<T*> { static const bool value = true; };
template <typename T> struct is_ptr<T* const> { static const bool value = true; };
template <typename T> struct is_ptr<T* volatile> { static const bool value = true; };
template <typename T> struct is_ptr<T* const volatile> { static const bool value = true; };
template<bool b>
struct bool_constant {};
template<typename T>
struct Box {
T data;
~Box() {
destroy(bool_constant<is_ptr<T>::value>{});
}
private:
void destroy(bool_constant<true>) {
delete data;
}
void destroy(bool_constant<false>) {} // nothing to do
};
Demo
However, this pretty much amounts to recreating the standard type traits, but probably worse. Just use the standard library when possible.
I think you had the right idea with the helper type, but I'd do it like the following example illustrates.
template <typename B, typename T>
struct BoxTraits {
static T& get_data(B *const box) { return box->data; }
// ^--- important
static T const& get_data(B const* const box) { return box->data; }
};
template <typename T>
struct BoxTraits<Box<T*>, T> {
static T& get_data(Box<T*>* const box) { return *box->data; }
static T const& get_data(Box<T*> const* const box) { return *box->data; }
};
Both versions always return T, so you can use them the same regardless of your Box's payload. You could add a type alias in Box so you don't have to pass the template arguments:
typedef Traits BoxTraits<Box, T>; // in Box class
Related
Suppose I have a method which is simplified to this
template<typename t,typename u>
std::shared_ptr<bar> MyClass::getFunct(std::string SomeStr)
{
.....
std::map<std::string,std::shared_ptr<foo> > j;
....
std::shared_ptr<u> collection(new u());
for (auto val : j){
val.second->getMethodA() //Will return object of type t <----LINE A
}
}
Now I am using it as
getFunct<FirstType>("SomeString")
getFunct<SecondType>("SomeString")
getFunct<ThirdType>("SomeString")
Now val.second in Line A
has 3 methods in it
val.second->getMethodA() //returns a type of FirstType
val.second->getMethodB() //returns a type of SecondType
val.second->getMethodC() //returns a type of ThirdType
Currently i am using
val.second->getMethodA() with template type FirstType
is there anyway for me to specify to use getMethodB if template type is SecondType
and use getMethodC if template type is ThirdType
The simplest solution is to replace the three getMethodX member functions with a single template function template<class T> T foo::getMethod(). Then create specializations for each type, if needed.
But if that is not appropriate for the design, then you can use a wrapper function instead:
template<class T>
struct helper {};
template<>
struct helper<FirstType> {
static FirstType getMethod(foo& f) {
return f.getMethodA();
}
};
// repeat specializations for other member functions
With C++17 you can use constexpr if:
template<typename T>
decltype(auto) foo(Bar& bar){
if constexpr(std::is_same_v<T,FirstType>){
return bar.getMethodA();
}
if constexpr(std::is_same_v<T,SecondType>){
return bar.getMethodB();
}
if constexpr(std::is_same_v<T,ThirdType>){
return bar.getMethodC();
}
}
In the absence of C++17 I would probably go for something simple like this:
template <typename T> struct type {};
struct select
{
bar &b;
decltype(auto) operator()(type<FirstType>) const { return b.getMethodA(); }
decltype(auto) operator()(type<SecondType>) const { return b.getMethodB(); }
decltype(auto) operator()(type<ThirdType>) const { return b.getMethodC(); }
};
select{*val.second}(type<T>{});
In the context of your example:
template <typename T> struct type {};
template<typename t,typename u>
std::shared_ptr<bar> MyClass::getFunct(std::string SomeStr)
{
.....
std::map<std::string,std::shared_ptr<foo> > j;
....
for (auto val : j) {
struct select {
bar &b;
decltype(auto) operator()(type<FirstType>) const { return b.getMethodA(); }
decltype(auto) operator()(type<SecondType>) const { return b.getMethodB(); }
decltype(auto) operator()(type<ThirdType>) const { return b.getMethodC(); }
};
select{*val.second}(type<t>{});
}
}
I'm trying to create a class which will contain a map of type_index keys mapped to pointers of each type passed as a template argument. This would allow me to specify a series of types my class will rely on in it's declaration.
I've done a bit of research but can only seem to find ways to unpack arguments, rather than types. I'm new to this subject, and would appreciate any clarification on terminology, or references to relevant text.
template <typename T>
T* SomeFakeFactoryGetter() { return new T(); }
template <class... Injected>
class UtilityProvider
{
public:
template <class U>
U* GetUtility()
{
std::type_index idx = std::type_index(typeid(U));
assert(_injectedClasses.find(idx) != _injectedClasses.end());
return reinterpret_cast<U*>(_injectedClasses[idx]);
}
// **
// How would I *unpack* all types for use as indices into my map?
// ( I realise this function is not what I want.)
template <Injected... C>
void Unpack()
{
_injectedClasses[std::type_index(typeid(C))] = SomeFakeFactoryGetter<C>();
}
private:
typedef std::unordered_map<std::type_index, void*> InjectedMap;
InjectedMap _injectedClasses;
};
class Bar{ public: void A() { printf("Hello bar"); } };
class Baz{ public: void B() { printf("Hello baz"); } };
class Nope {};
class Foo : public UtilityProvider<Bar, Baz>
{
public:
Foo()
{
GetUtility<Bar>()->A();
GetUtility<Nope>(); // Fail. User must specify which utilities this class will use.
}
};
What I've done in this situation is to create a dummy function to expand these expressions into, but it looks quite hideous:
template <int ... Dummies>
void dummy(int&& ...){}
template <class ... C>
void Unpack()
{
dummy(((_injectedClasses[std::type_index(typeid(C))] =
SomeFakeFactoryGetter<C>()), 0)...);
}
Note that in your case I think you'll be better off with using insert with an initializer_list:
template <class ... C>
void Unpack()
{
_injectedClasses.insert({std::make_pair(std::type_index(typeid(C)),
SomeFakeFactoryGetter<C>())...});
}
I couldn't find a direct mention of this but I believe there is an important difference between the two methods, in case you didn't already know. insert will not override existing key-value pairs, whereas operator[] will. This can affect which method you should use if if this is important to you.
An alternative approach:
template <typename ... C> struct Unpacker;
template <typename Tail, typename ... Queue>
struct Unpacker<Tail, Queue...>
{
void operator () (InjectedMap& injectedClasses) const
{
_injectedClasses[std::type_index(typeid(Tail))] = SomeFakeFactoryGetter<Tail>();
Unpacker<Queue...>()(injectedClasses);
}
};
template <>
struct Unpacker<>
{
void operator () (InjectedMap& injectedClasses) const {}
};
I have a typical type-erasure setup:
struct TEBase
{
virtual ~TEBase() {}
// ...
};
template <typename T>
struct TEImpl : TEBase
{
// ...
};
Now the question: Given a second class hierarchy like this,
struct Foo { };
struct Bar : Foo { };
struct Unrelated { };
is it possible, given a TEBase * p, to determine whether the dynamic type of *p is of the form TEImpl<X>, where, X derives from Foo? In other words, I want function:
template <typename T> bool is_derived_from(TEBase * p);
such that:
is_derived_from<Foo>(new TEImpl<Foo>) == true
is_derived_from<Foo>(new TEImpl<Bar>) == true
is_derived_from<Foo>(new TEImpl<Unrelated>) == false
In particular, I'm looking for a solution that is general, non-intrusive, and efficient. I've found two solutions to this problem (posted below as answers) but neither of them solve all three criteria.
Something like this:
template <typename Type, typename UnaryPredicate>
void DoPred(UnaryPredicate pred)
{
if (T * p = dynamic_cast<Derived<T> *>(this))
{
return pred(p->type);
}
return false;
}
This isn't 100% universal, since you cannot, for example, say DoPred<int>. A more universal solution would add a virtual std::type_info type() const { return typeid(...); } member function to the hierarchy and use that to determine if the type matches (the standard type erasure idiom). Both approaches use the same sort of RTTI, though.
After the clarification:
Right now, I don't think this can be solved. All you have is a TEBase subobject. It could be part of a TEImpl<Bar>, or part of a TEImpl<Unrelated>, but neither of those types is related to TEImpl<Foo>, which is what you're after.
You're essentially asking that TEImpl<Bar> derives from TEImpl<Foo>. To do this, you would actually want TEImpl<T> to inherit from all TEImpl<std::direct_bases<T>::type>..., if you see what I mean. This is not possible in C++11, but will be possible in TR2. GCC already supports it. Here is an example implementation. (It causes a warning due to ambiguous bases, which could be avoided with more work, but it works nonetheless.)
#include <tr2/type_traits>
struct TEBase { virtual ~TEBase() {} };
template <typename T> struct TEImpl;
template <typename TL> struct Derivator;
template <typename TL, bool EmptyTL>
struct DerivatorImpl;
template <typename TL>
struct DerivatorImpl<TL, true>
: TEBase
{ };
template <typename TL>
struct DerivatorImpl<TL, false>
: TEImpl<typename TL::first::type>
, Derivator<typename TL::rest::type>
{ };
template <typename TL>
struct Derivator
: DerivatorImpl<TL, TL::empty::value>
{ };
template <typename T>
struct TEImpl
: Derivator<typename std::tr2::direct_bases<T>::type>
{
};
template <typename T>
bool is(TEBase const * b)
{
return nullptr != dynamic_cast<TEImpl<T> const *>(b);
}
struct Foo {};
struct Bar : Foo {};
struct Unrelated {};
#include <iostream>
#include <iomanip>
int main()
{
TEImpl<int> x;
TEImpl<Unrelated> y;
TEImpl<Bar> z;
TEImpl<Foo> c;
std::cout << std::boolalpha << "int ?< Foo: " << is<Foo>(&x) << "\n";
std::cout << std::boolalpha << "Unr ?< Foo: " << is<Foo>(&y) << "\n";
std::cout << std::boolalpha << "Bar ?< Foo: " << is<Foo>(&z) << "\n";
std::cout << std::boolalpha << "Foo ?< Foo: " << is<Foo>(&c) << "\n";
}
I would suggest reading the article Generic Programming:Typelists and Applications. There Andrei Alexandrescu desribes an implementation of a ad-hoc Visitor which should solve your problem. Another good resource would be his book Moder C++ Design where he describes a multidispatcher in a Brute Force way which uses the same approuch (pages 265 ...).
In my opinion these 2 resources are better for understanding than any code which could be printed here.
This solution involves abusing exceptions a bit. If the TEImpl type simply throws its data, is_derived_from can catch the type it's looking for.
struct TEBase
{
virtual ~TEBase() {}
virtual void throw_data() = 0;
};
template <typename T>
struct TEImpl : public TEBase
{
void throw_data() {
throw &data;
}
T data;
};
template <typename T>
bool is_derived_from(TEBase* p)
{
try {
p->throw_data();
} catch (T*) {
return true;
} catch (...) {
// Do nothing
}
return false;
}
This solution works great. It works perfectly with any inheritance structure, and it's completely non-intrusive.
The only problem is that it's no efficient at all. Exceptions were not intended to be used in this way, and I suspect this solution is thousands of times slower than other solutions.
This solution involves comparing typeids. TEImpl knows its own type, so it can check a passed typeid against its own.
The trouble is, this technique doesn't work when you add inheritance, so I'm also using template meta-programming to check if the type has typedef super defined, in which case it will recursively check its parent class.
struct TEBase
{
virtual ~TEBase() {}
virtual bool is_type(const type_info& ti) = 0;
};
template <typename T>
struct TEImpl : public TEBase
{
bool is_type(const type_info& ti) {
return is_type_impl<T>(ti);
}
template <typename Haystack>
static bool is_type_impl(const type_info& ti) {
return is_type_super<Haystack>(ti, nullptr);
}
template <typename Haystack>
static bool is_type_super(const type_info& ti, typename Haystack::super*) {
if(typeid( Haystack ) == ti) return true;
return is_type_impl<typename Haystack::super>(ti);
}
template <typename Haystack>
static bool is_type_super(const type_info& ti, ...) {
return typeid(Haystack) == ti;
}
};
template <typename T>
bool is_derived_from(TEBase* p)
{
return p->is_type(typeid( T ));
}
For this to work with, Bar needs to be redefined as:
struct Bar : public Foo
{
typedef Foo super;
};
This should be fairly efficient, but it's obviously not non-intrusive, since it requires a typedef super in the target class whenever inheritance is being used. The typedef super also has to be publicly accessible, which goes against what many consider to be a recommended practice of putting your typedef super in your private section.
It also doesn't deal with multiple-inheritance at all.
Update: This solution can be taken further to make it general and non-intrusive.
Making it general
typedef super is great, because it's idiomatic and already used in many classes, but it doesn't allow multiple inheritance. In order to do that, we'll need to replace it with a type that can store multiple types, such as a tuple.
If Bar was rewritten as:
struct Bar : public Foo, public Baz
{
typedef tuple<Foo, Baz> supers;
};
we could support this form of declaration by adding the following code to TEImpl:
template <typename Haystack>
static bool is_type_impl(const type_info& ti) {
// Redefined to call is_type_supers instead of is_type_super
return is_type_supers<Haystack>(ti, nullptr);
}
template <typename Haystack>
static bool is_type_supers(const type_info& ti, typename Haystack::supers*) {
return IsTypeTuple<typename Haystack::supers, tuple_size<typename Haystack::supers>::value>::match(ti);
}
template <typename Haystack>
static bool is_type_supers(const type_info& ti, ...) {
return is_type_super<Haystack>(ti, nullptr);
}
template <typename Haystack, size_t N>
struct IsTypeTuple
{
static bool match(const type_info& ti) {
if(is_type_impl<typename tuple_element< N-1, Haystack >::type>( ti )) return true;
return IsTypeTuple<Haystack, N-1>::match(ti);
}
};
template <typename Haystack>
struct IsTypeTuple<Haystack, 0>
{
static bool match(const type_info& ti) { return false; }
};
Making it non-intrusive
Now we have a solution which is efficient and general, but it's still intrusive, so it won't support classes that can't be modified.
To support this, we'll need a way to declare the object inheritance from outside the class. For Foo, we could do something like this:
template <>
struct ClassHierarchy<Bar>
{
typedef tuple<Foo, Baz> supers;
};
To support that style, first we need the non-specialized form of ClassHierarchy, which we'll define like so:
template <typename T> struct ClassHierarchy { typedef bool undefined; };
We'll use the presence of undefined to tell whether or not the class has been specialized.
Now we need to add some more functions to TEImpl. We'll still reuse most of the code from earlier, but now we'll also support reading the type data from ClassHierarchy.
template <typename Haystack>
static bool is_type_impl(const type_info& ti) {
// Redefined to call is_type_external instead of is_type_supers.
return is_type_external<Haystack>(ti, nullptr);
}
template <typename Haystack>
static bool is_type_external(const type_info& ti, typename ClassHierarchy<Haystack>::undefined*) {
return is_type_supers<Haystack>(ti, nullptr);
}
template <typename Haystack>
static bool is_type_external(const type_info& ti, ...) {
return is_type_supers<ClassHierarchy< Haystack >>(ti, nullptr);
}
template <typename Haystack>
struct ActualType
{
typedef Haystack type;
};
template <typename Haystack>
struct ActualType<ClassHierarchy< Haystack >>
{
typedef Haystack type;
};
template <typename Haystack>
static bool is_type_super(const type_info& ti, ...) {
// Redefined to reference ActualType
return typeid(typename ActualType<Haystack>::type) == ti;
}
And now we have a solution which is efficient, general, and non-intrusive.
Future solution
This solution meets the criteria, but it's still a little annoying to have to document the class hierarchy explicitly. The compiler already knows everything about the class hierarchy, so it's a shame that we have to do this grunt work.
A proposed solution to this problem is N2965: Type traits and base classes, which has been implemented in GCC. This paper defines a direct_bases class, which is almost identical to our ClassHierarchy class, except its only element, type, is guaranteed to be a tuple, like supers, and the class is completely generated by the compiler.
So for now we have to write a little boilerplate to get this to work, but if N2965 gets accepted, we can get rid of the boilerplate and make TEImpl much shorter.
Special thanks to Kerrek SB and Jan Herrmann. This answer drew a lot of inspiration from their comments.
I have an application that consists of multiple tasks that share common data using shared memory. Up to now the data in shared memory look like that:
struct Store = {
int id;
Array<Module, 5> modules;
};
where Module is defined as
struct Module = {
uint32_t a;
char b[64];
Array<Component, 10> components;
};
This Store structure has a fixed size an can be easily used within shared memory.
But now I have to support other Modules, lets say ModuleA and ModuleB. Within the normal C++ context I would model these as:
struct ModuleBase {
// common informations
};
struct ModuleA : public ModuleBase {
// ...
};
struct ModuleB : public ModuleBase {
// ...
};
and replace Module by Module* in the Store.
But within the shared memory this is not so easy.
Accessing data in shared memory is easy for fix structures that's why a compile time array is used. I would like to have this property with my different module's.
Idea 1
union Module {
ModuleType type;
ModuleA moduleA;
ModuleB moduleB;
};
The problem is that my Module classes have constructors and that is not allowed inside the union. Access is easy using the type and then Module.moduleX
fix: remove need of constructors
Idea 2
Using a template that evaluates the maximum size of given classes, e.g.
const size_t max_module_size = MaxTMP<ModuleA, ModuleB>::value;
This is the size of the buffer I need to store the modules:
char ModuleBuffer[max_module_size];
(maybe the ModuleBuffer has to be encapsulated in a struct, for usage with Array)
Access is tricky, the content of ModuleBuffer has to be casted to ModuleBase and according to the type to ModuleX. That for I think I need some reinterpret_cast. And I also need to cast the 'ModuleX' in some way to put into the ModuleBuffer.
Question
I don't like both ideas but I cannot imagine another way to handle this problem. Do you have any comments or - even better - solutions?
Effectively, you are between a rock and a hard place.
I would give a try to boost::variant, because of the facilities it comes with, otherwise it's not too difficult to recreate a similar thing, but it is long...
On top of size, you also need to take care about alignment. It will help to use C++11 here, although it is possible to write this in C++03 with a couple libraries/extensions.
Note that a union is not anything so special, and you can easily implement your own, in a way, and like boost::variant make it "tagged".
A couple helpers will help nicely:
/// Size and Alignment utilties
constexpr size_t max(size_t t) { return t; }
template <typename... U>
constexpr size_t max(size_t l, size_t r, U... tail) {
return l > max(r, tail...) ? l : max(r, tail...);
}
template <typename... T>
struct size { static size_t const value = max(sizeof(T)...); };
template <typename... T>
struct alignment { static size_t const value = max(alignof(T)...); };
/// Position of a type in the list
template <typename...> struct position;
template <typename T>
struct position<T> {
static size_t const value = 0;
};
template <typename T, typename Head, typename... Tail>
struct position<T, Head, Tail...> {
static size_t const value =
std::is_same<T, Head>::value ? 0 : 1 + position<T, Tail...>::value;
};
/// Type at a given position
template <size_t, typename...> struct at;
template <size_t N, typename T, typename... Tail>
struct at<N, T, Tail...> { typedef typename at<N-1, Tail..>::type type; };
template <typename T, typename... Tail>
struct at<0, T, Tail...> { typedef T type; };
Now the true fun starts: how to apply a function in a typesafe manner with a type that may change at runtime :x ?
/// Function application
template <typename...> struct Apply;
template <typename H, typename... Tail>
struct Apply<H, Tail...> {
// Mutable
template <typename Func>
static void Do(Func& f, void* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
template <typename Func>
static void Do(Func const& f, void* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
// Const
template <typename Func>
static void Do(Func& f, void const* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H const*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
template <typename Func>
static void Do(Func const& f, void const* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H const*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
}; // struct Apply
/// We need recursion to end quietly even though `tag` is a runtime argument
/// we place the precondition that `tag` should be a valid index in the type
/// list so this should never be reached.
template <>
struct Apply<> {
template <typename... T>
static void Do(T...&&) { abort(); }
}; // struct Apply
Now we can use this to dynamically dispatch in a type safe manner.
/// Variant itself
template <typename... List>
class Variant {
public:
/// Constructor & co
Variant() {
typedef typename at<0, List...>::type First;
new (&_storage) First();
}
Variant(Variant const& other) {
this->initialize(other);
}
Variant& operator=(Variant const& other) {
this->destroy();
this->initialize(other);
return *this;
}
~Variant() { this->destroy(); }
/// Conversions
template <typename T>
explicit Variant(T const& t) {
_tag = position<T, List...>::value;
new (&_storage) T(t);
}
template <typename T>
Variant& operator=(T const& t) {
_tag = position<T, List...>::value;
this->destroy();
new (&_storage) T(t);
return *this;
}
/// Applying a func
template <typename Func>
void apply(Func& f) { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func& f) const { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func const& f) { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func const& f) const { Apply<List...>::Do(f, &_storage, _tag); }
private:
void initialize(Variant const& v) {
struct {
template <typename T>
void operator()(T& t) const { new (_storage) T(t); }
void* _storage;
} copier = { &_storage };
v.apply(copier);
_tag = v._tag;
}
void destroy() {
struct {
template <typename T>
void operator()(T& t) const { t.~T(); }
} eraser;
this->apply(eraser);
}
std::aligned_storage<size<List...>::value,
alignment<List...>::value> _storage;
size_t _tag;
}; // class Variant
Did I say easy ?
Well, there is a subtle issue still: the operator= implementations are not exception safe. In your case it should not be an issue, since you do not have dynamic memory allocation in those types.
References:
std::aligned_storage
I want to automatically choose the right pointer-to-member among overloaded ones based on the "type" of the member, by removing specializations that accept unconcerned members (via enable_if).
I have the following code:
class test;
enum Type
{
INT_1,
FLOAT_1,
UINT_1,
CHAR_1,
BOOL_1,
INT_2,
FLOAT_2,
UINT_2,
CHAR_2,
BOOL_2
};
template<typename T, Type Et, typename func> struct SetterOk { static const bool value = false; };
template<typename T> struct SetterOk<T,INT_1,void (T::*)(int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,FLOAT_1,void (T::*)(float)> { static const bool value = true; };
template<typename T> struct SetterOk<T,UINT_1,void (T::*)(unsigned int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,CHAR_1,void (T::*)(char)> { static const bool value = true; };
template<typename T> struct SetterOk<T,BOOL_1,void (T::*)(bool)> { static const bool value = true; };
template<typename T> struct SetterOk<T,INT_2,void (T::*)(int,int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,FLOAT_2,void (T::*)(float,float)> { static const bool value = true; };
template<typename T> struct SetterOk<T,UINT_2,void (T::*)(unsigned int, unsigned int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,CHAR_2,void (T::*)(char,char)> { static const bool value = true; };
template<typename T> struct SetterOk<T,BOOL_2,void (T::*)(bool,bool)> { static const bool value = true; };
template <bool, class T = void> struct enable_if {};
template <class T> struct enable_if<true, T> { typedef T type; };
template<typename T, Type Et>
struct Helper
{
template<typename U>
static void func(U method, typename enable_if<SetterOk<T,Et,U>::value>::type* dummy = 0)
{
}
};
class test
{
public:
void init()
{
Helper<test,INT_2>::func(&test::set);
}
void set2(int);
void set(int);
void set(int,int);
void set(float,float);
};
int main()
{
test t;
t.init();
return 0;
}
I'm expecting it to choose the right function between all possible. The problem is that the compiler says "cannot deduce template argument as function argument is ambiguous".
It seems I don't know how to use enable_if, because if so the compiler would only allow the specialization if the specified function has the right type...
Note that I want to have C++03 solutions (if possible) - my code must compile on some old compilers.
Thanks in advance
You can never refer to an overloaded function without disambiguating it (means: static_casting it to the correct type). When you instantiate Helper::func the type of the function argument cannot be known without ever disambiguating it.
The reason it doesn't compile is quite simply that there are several different overloaded functions and it doesn't know which one you mean. Granted, only one of these (void set(int,int)) would actually compile, given the specialization Helper<test,INT_2>. However, this is not enough for the compiler to go on.
One way of getting this to compile would be to explicitly cast &test::set to the appropriate type:
Helper<test,INT_2>::func(static_cast<void (test::*)(int,int)>(&test::set));
Another way would be to use explicit template specialization:
Helper<test,INT_2>::func<void (test::*)(int,int)>((&test::set));
Either way, you need to let the compiler know which of the set functions you are trying to refer to.
EDIT:
As I understand it, you want to be able to deduce, from the use of a Type, which function type should be used. The following alternative achieves this:
template<typename T, Type Et> struct SetterOK{};
template<typename T> struct SetterOK<T,INT_1> {typedef void (T::*setter_type)(int);};
template<typename T> struct SetterOK<T,FLOAT_1> {typedef void (T::*setter_type) (float);};
// ...
template<typename T> struct SetterOK<T,INT_2> {typedef void (T::*setter_type)(int,int);};
// ....
template<typename T, Type Et>
struct Helper
{
template<typename U>
static void func(U method)
{
}
};
class test
{
public:
void init()
{
Helper<test,INT_2>::func<SetterOK<test,INT_2>::setter_type >(&test::set);
}
void set2(int);
void set(int);
void set(int,int);
void set(float,float);
};
int main()
{
test t;
t.init();
return 0;
}
ADDITIONAL EDIT:
A thought just occurred to me. In this special case which you've done, where U is SetterOK::setter_type, things can be simplified further by completely removing the template arguments for func:
static void func(typename SetterOK<T,Et>::setter_type method)
{
}
This would make the init method a simpler:
void init()
{
Helper<test,INT_2>::func(&test::set);
}