Reputation: 713
In my program I have a lot of pointers to functions provided by an external library. Some of them return value, and some of them not.
If external library did not provide function (pointer is NULL) and function should return value program should assume default value (which is known at compile time).
What I try to accomplish is to reduce amount of if
statements and to wrap pointer in class with following use case:
enum E { e1, e2, e3 };
UserFunction<uint8_t(int, int), 0> callback1 { user_function_ptr }; // Calls and returns value of user_function_ptr
UserFunction<uint8_t(), 0> callback2 { nullptr }; // Returns 0
UserFunction<uint8_t(), 1> callback3 { nullptr }; // Returns 1
UserFunction<E(int), e1> callback4 { user_function_ptr2 }; // Returns enum value and takes one integer argument
UserFunction<void(int)> callback5 { user_function_ptr3 }; // No return value, one argument
UserFunction<void()> callback6 { nullptr }; // Should perform noop
What I got so far is working for functions that return value:
template <class Sign, int Def>
struct UserF;
template <class R, int Def, class... Args>
struct UserF<R(Args...), Def> {
typedef R Signature(Args...);
typedef typename std::add_pointer<Signature>::type SignaturePtr;
static R Default(Args... args) {
return (R) Def;
}
UserF() {
functionToCall = Default;
}
UserF(SignaturePtr userFunction) {
if (userFunction != nullptr) {
functionToCall = userFunction;
} else {
functionToCall = Default;
}
}
R operator() (Args... args) {
return functionToCall(args...);
}
private:
SignaturePtr functionToCall;
};
The problem with code above is that it forces default value to be int
. What I could do is change UserF
template to something like this:
template <class R, R Def, class... Args>
struct UserF {
// ...
};
// use case
UserF<E, e1, int> callback; // Returns E, takes int, default e1
But if it possible I would rather use
UserF<R(Args...), Default> callback; // preferred
UserF<Default, R(Args...)> callback; // if above is not possible
UserF<void(Args...)> callback; // if no return value
I would rather not use std::function
since I know that I will be dealing only with pointers to functions, and not with pointers to member functions, functor objects, etc. Also boost is not allowed (C++11 is allowed).
To summarize, the question is: How to force type check on default return value.
Upvotes: 2
Views: 137
Reputation: 907
If you can stomach a small macro, you could do this:
template <typename sig, typename T, T v >
struct UserF_;
template <class R, typename ... Args, typename T, T v >
struct UserF_< R(Args...), T, v > {
// ...
};
#define UserF( F, Default ) UserF_< F, decltype( Default ), Default >
UserF(R(Args...), Default) callback;
You cannot have a generic template non-type parameter without first specifying its type as a template type parameter.
Upvotes: 1
Reputation: 275320
template<class Sig>
struct return_type;
template<class Sig>
using return_type_t=typename return_type<Sig>::type;
template<class R,class...Args>
struct return_type<R(Args...)>{
using type=R;
};
template <class Sign, return_type_t<Sign> Def>
struct UserF;
is most of it. To handle void
you'll want a flag trick:
struct void_flag{};
template<class T>
using flag_void=
typename std::conditional<std::is_same<T,void>{},void_flag*,T>::type;
template <class Sign, flag_void<return_type_t<Sign>> Def=nullptr>
struct UserF;
template <class R, R Def, class... Args>
struct UserF<R(Args...), Def>{
// body
template <class... Args>
struct UserF<void(Args...), 0>{
// body
there will be problems with non-integral non-pointer R
, as you cannot pass a double
to a template
. One approach would be to upgrade double
to double const*
for Def
, and have it auto-dereference:
template <class R, R const* Def, class... Args>
struct UserF<R(Args...), Def>{
// body
and do something like flag_void
to turn T=double
into T=double const*
.
Upvotes: 1