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);
}
Related
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
Trying to accomplish the following:
// Template not necessary, but shows the pattern
template <typename T>
bool MyFunction(const T&, const uint8_t);
template <T>
struct is_myfunc_defined : std::false_type{}
// How do I properly create this
template <typname R, typename... Args>
struct is_myfunc_defined<R MyFunction(args....)> : std::true_type
{
};
struct MyStruct1 {};
struct MyStruct2 {};
// Will Match
bool MyFunction(const MyStruct&, const uint8_t){ return true; }
// Will not match
bool ShouldFail1(const MyStruct2&, const uint8_t){ return true; }
void MyFunction(const MyStruct2&, const uint8_t){ return true; }
bool MyFunction(const MyStruct2&){ return true; }
int main()
{
cout << is_myfunc_defined<MyStruct>::value << endl; // true
cout << is_myfunc_defined<MyStruct2>::value << endl; // false
}
I know how to use is_detected_exact to check for a class method with a specific return type, name, and signature, but how does one do it with a straight up function. Can't figured it out, need help.
Thanks!
I know how to use is_detected_exact to check for a class method
It's no different for a global function:
template <typename ...P>
using detect_myfunc = decltype(MyFunction(std::declval<P>()...));
template <typename T>
struct is_myfunc_defined {};
template <typename R, typename ...P>
struct is_myfunc_defined<R(P...)>
: std::experimental::is_detected_exact<R,detect_myfunc,P...> {};
{};
I'm trying to modernise some GStreamer code by adding smart pointers. So for instance:
GstElement *pipeline = gst_pipeline_new("test-pipeline");
gst_object_unref(pipeline);
can be rewritten:
struct GstElementDeleter {
void operator()(GstElement* p) { gst_object_unref(p); }
};
std::unique_ptr<GstElement, GstElementDeleter> pipeline = gst_pipeline_new("test-pipeline");
But gst_object_unref() can be used on any gpointer so it can be rewritten:
template<typename T>
struct GPointerDeleter {
void operator()(T* p) { gst_object_unref(p); }
};
std::unique_ptr<GstElement, GPointerDeleter<GstElement>> pipeline = gst_pipeline_new("test-pipeline");
But what I'd like to do is limit this to only handling types that can be deallocated using gst_object_unref. Is there a way of declaring a template to only work with a list of types - GstElement, GstBus, etc?
Maybe you could make template the operator() (so there is no need to explicit the template parameter defining the smart pointer) and use SFINAE to enable the operator() only for the allowed types
struct GPointerDeleter
{
template <typename T>
typename std::enable_if<std::is_same<T, GstElement>::value
|| std::is_same<T, GstBus>::value
/* or other cases */
>::type operator() (T * p) const
{ gst_object_unref(p); }
};
Or, maybe better, you can add (as suggested by Jarod42 (thanks)) a static_assert() check inside the operator()
struct GPointerDeleter
{
template <typename T>
void operator() (T * p) const
{
static_assert( std::is_same<T, GstElement>::value
|| std::is_same<T, GstBus>::value
/* or other cases */, "some error message" );
gst_object_unref(p);
}
};
Perhaps a type trait? See <type_traits> if you haven't seen these before.
template<typename T>
struct can_gst_unref : std::false_type { };
// for each type...
template<> struct can_gst_unref<GstElement> : std::true_type { };
// convenient alias, as is convention for type traits
template<typename T>
inline constexpr bool can_gst_unref_v = can_gst_unref<T>::value;
// now conditionally define operator() in your deleter
struct GstDeleter {
template<typename T>
std::enable_if_t<can_gst_unref_v<T>> operator()(T* p) { gst_object_unref(p); }
};
// Making the function a template instead of the class reduces clutter at usage
std::unique_ptr<GstElement, GstDeleter> works(gst_pipeline_new("test-pipeline"));
// can_gst_unref is not specialized to std::string
// so the general case takes over, and gives can_gst_unref_v<std::string> = false
// std::enable_if_t thus doesn't produce a type, and operator() is not defined, because it has no return type
// therefore, this doesn't compile
std::unique_ptr<std::string, GstDeleter> whoops;
Suppose I have a template function:
template<typename T>
T produce_5_function() { return T(5); }
How can I pass this entire template to another template?
If produce_5_function was a functor, there would be no problem:
template<typename T>
struct produce_5_functor {
T operator()() const { return T(5); }
};
template<template<typename T>class F>
struct client_template {
int operator()() const { return F<int>()(); }
};
int five = client_template< produce_5_functor >()();
but I want to be able to do this with a raw function template:
template<??? F>
struct client_template {
int operator()() const { return F<int>(); }
};
int five = client_template< produce_5_function >()();
I suspect the answer is "you cannot do this".
I suspect the answer is "you cannot do this".
Yes, that is the case, you cannot pass a function template as a template argument. From 14.3.3:
A template-argument for a template template-parameter shall be the
name of a class template or an alias template, expressed as
id-expression.
The template function needs to be instantiated before you pass it to the other template. One possible solution is to pass a class type that holds a static produce_5_function like so:
template<typename T>
struct Workaround {
static T produce_5_functor() { return T(5); }
};
template<template<typename>class F>
struct client_template {
int operator()() const { return F<int>::produce_5_functor(); }
};
int five = client_template<Workaround>()();
Using alias templates, I could get a little closer:
template <typename T>
T produce_5_functor() { return T(5); }
template <typename R>
using prod_func = R();
template<template<typename>class F>
struct client_template {
int operator()(F<int> f) const { return f(); }
};
int five = client_template<prod_func>()(produce_5_functor);
How about wrapping that function?
template<typename T>
struct produce_5_function_wrapper {
T operator()() const { return produce_5_function<T>(); }
};
Then you can use the wrapper instead of the function:
int five = client_template< produce_5_function_wrapper >()();
Using the template function alone will not work, there's no such thing as "template template functions".
template<typename T1, typename T2>
class Bimap {
public:
class Data {
private:
template<typename T> Data& set(T);
template<> Data& set<T1>(typename T1 v) { /*...*/ }
};
};
That gives me the error:
error: explicit specialization in non-namespace scope 'class Bimap<T1, T2>::Data'
I understand what the error is saying. But why I can't I do this? And how can I fix it?
One way forget templates, overload:
Data& set(T1 v) { /*...*/ }
but here is a trick which I use sometimes
you can specialize class template within class:
class {
template<typename T>
struct function_ {
static void apply(T);
};
template<>
struct function_<int> {
...
};
template<typename T>
void function(T t) { return function_<T>::apply(t); }
#Albert
I had a similar problem when I wanted to add a "trim-excess-capacity" to a custom made container. The std::vector swap trick and changing the declaration of the existing container were not valid options. So I've come up with this:
template <class T, bool isPtr> struct DeleteImp
{
static void Trim(T* to, unsigned int count);
};
template <class T> struct DeleteImp<T, false>
{
static void Trim(T* to, unsigned int count) {}
};
template <class T> struct DeleteImp<T, true>
{
static void Trim(T* to, unsigned int count)
{
for(unsigned int i=0; i<count; i++)
delete to[i];
}
};
used by my container like this:
DeleteImp<T, TypeTraits<T>::isPointer>::Trim(buf + length, truelength-length);
You may also want to check out this resource.