I am writing a class member function that will take a lambda with a given type T in the function argument. My question is: is it possible to overload the member function at compile-time based on the mutability of the argument? Below is the example:
// T is a given type for class.
template <typename T>
class Wrapper {
T _t;
// For T&
template <typename F, typename R = std::result_of_t<F(T&)>>
std::enable_if_t<std::is_same<R, void>::value> operator()(F&& f) {
f(_t);
}
// For const T&
template <typename F, typename R = std::result_of_t<F(const T&)>>
std::enable_if_t<std::is_same<R, void>::value> operator()(F&& f) const {
f(_t);
}
};
So, what I want is, if the give lambda is with the following signature, the first operator should be invoked.
[](T&) {
...
};
For constant argument, the second should be invoked.
[](const T&) {
}
If you plan to use non-capturing lambdas only, you can rely on the fact that they decay to pointers to functions.
It follows a minimal, working example:
#include<type_traits>
#include<iostream>
template <typename T>
class Wrapper {
T _t;
public:
auto operator()(void(*f)(T &)) {
std::cout << "T &" << std::endl;
return f(_t);
}
auto operator()(void(*f)(const T &)) const {
std::cout << "const T &" << std::endl;
return f(_t);
}
};
int main() {
Wrapper<int> w;
w([](int &){});
w([](const int &){});
}
Otherwise you can use two overloaded functions as it follows:
#include<type_traits>
#include<iostream>
#include<utility>
template <typename T>
class Wrapper {
T _t;
template<typename F>
auto operator()(int, F &&f)
-> decltype(std::forward<F>(f)(const_cast<const T &>(_t))) const {
std::cout << "const T &" << std::endl;
return std::forward<F>(f)(_t);
}
template<typename F>
auto operator()(char, F &&f) {
std::cout << "T &" << std::endl;
return std::forward<F>(f)(_t);
}
public:
template<typename F>
auto operator()(F &&f) {
return (*this)(0, std::forward<F>(f));
}
};
int main() {
Wrapper<int> w;
w([](int &){});
w([](const int &){});
}
Related
I have a templated function meant to work with STL containers. However, I'd like to specialize this function for a templated class. How might I go about doing this? I'm stuck on C++11.
Edit: Updated code example. It appears to work when F is a POD like int, but is broken in the below code example where F is a class.
#include <vector>
#include <iostream>
template <class F>
class Temp {
private:
F f;
public:
Temp(F f) : f(f) {}
};
template <typename C, typename F>
struct MyClass {
MyClass (const C& c, const F& f) { }
operator C() const
{ return {}; }
};
// Base Template Function
template <typename C, typename F>
MyClass<C, F> operator| (const C & left, const F & right)
{
std::cout << "Generic version" << std::endl;
return MyClass<C, F>(left, right);
}
// Overload that I tried (ignored and used base template function instead)
template <typename C, typename F>
MyClass<C, F> operator| (const MyClass<C, F> & left, const F & right)
{
std::cout << "MyClass overload" << std::endl;
return MyClass<C, F>(C(left), right);
}
template <class F>
Temp<F> filter(F f) {
return Temp<F>(f);
}
int main ()
{
std::vector<int> v0 = { 1, 2, 3 };
auto m0 = v0
| filter([] (int i) {return i > 0;});
auto m1 = m0
| filter([] (int i) {return i < 3;});
return 0;
}
The above code example prints Generic version twice.
You can't partially specialize a template function.
But your overload should works.
I've modified your MyClass class to make it compile but, in the following example, the overload version is in charge.
#include <iostream>
template <typename C, typename F>
struct MyClass
{
template <typename ... Ts>
MyClass (Ts const & ...)
{ }
operator C() const
{ return {}; }
};
/* Base Template Function */
template <typename C, typename F>
MyClass<C, F> operator| (C const & left, F const & right)
{
std::cout << "Generic version" << std::endl;
return MyClass<C, F>(left, right);
}
/* Overload that I tried (ignored and used base template function instead) */
template <typename C, typename F>
MyClass<C, F> operator| (MyClass<C, F> const & left, F const & right)
{
std::cout << "MyClass overload" << std::endl;
return MyClass<C, F>(C(left), right);
}
int main ()
{
MyClass<int, int> m0;
auto x = m0 | 0;
static_assert( std::is_same_v<decltype(x), MyClass<int, int>>, "!" );
}
I'd like to modernise a common technique I use or perhaps over use. It statically checks for method signatures and calls the methods if they exist. My approach predates C++17 by some time FWIW.
Currently, I used Boost's Type traits like BOOST_TTI_HAS_MEMBER_FUNCTION(event)
which allows something such as
template <typename M, typename E>
static inline typename std::enable_if<
has_member_function_event<current_t, void, boost::mpl::vector<M &, const E &>>::value
>::type
event(M &mux, S &g, const E &e) {
auto &node = boost::fusion::at<N>(g);
node.event(mux, e);
...
It works just fine but, you know, it's not the prettiest. Is there a way I might avoid the macros and join the rest of you in the modern world :-)?
Regards,
--Matt. (aka dinosaur)
Would simple, direct SFINAE suit your needs?
Here's a test function exercising various member functions, checking for adequate return types and const-correctness as well:
template <typename Obj> void exercise(Obj&& obj) {
if constexpr(has_bar(obj)) {
std::cout << "called T bar() -> something or void\n";
obj.bar();
}
if constexpr(converts<int>(has_foo(obj))) {
std::cout << "called int foo() const -> " << obj.foo() << "\n";
}
if constexpr(converts<long>(has_foo(obj, "some text"))) {
std::cout << "called long foo(std::string) -> " << obj.foo("some text") << "\n";
}
}
The has_bar implementation is simply:
template <typename T>
static constexpr auto has_bar(T&& obj) -> exists<decltype(obj.bar())> { return {}; }
template <typename... T>
static constexpr auto has_bar(T&&...) -> does_not_exist { return {}; }
To generically allow for checking signatures and avoid repetitious code, here's a helper macro (obviously optional):
#define DEF_HAS_MEMBER(name) \
template <typename T, typename... Args> \
static constexpr auto has_##name(T&& obj, Args&&... args) \
-> exists<decltype(std::forward<T>(obj).name(std::forward<Args>(args)...))> { return {}; } \
template <typename... T> \
static constexpr auto has_##name(T&&...) -> does_not_exist { return {}; }
DEF_HAS_MEMBER(foo)
DEF_HAS_MEMBER(bar)
The converts predicate now is an ultra-simple addition:
template <typename T, typename R>
static constexpr auto converts(R) { return std::is_convertible_v<typename R::return_type, T>; }
Everything together:
Live On Coliru
#include <string>
#include <type_traits>
#include <iostream>
template <typename R> struct exists : std::true_type { using return_type = R; };
struct does_not_exist : std::false_type { using return_type = void; };
#define DEF_HAS_MEMBER(name) \
template <typename T, typename... Args> \
static constexpr auto has_##name(T&& obj, Args&&... args) \
-> exists<decltype(std::forward<T>(obj).name(std::forward<Args>(args)...))> { return {}; } \
template <typename... T> \
static constexpr auto has_##name(T&&...) -> does_not_exist { return {}; }
DEF_HAS_MEMBER(foo)
DEF_HAS_MEMBER(bar)
struct Everything {
int foo(std::string /*unused*/) { return 42; }
int foo() const { return -1; }
void bar() {}
};
struct Some {
int foo() const { return -2; }
};
template <typename T, typename R>
static constexpr auto converts(R) { return std::is_convertible_v<typename R::return_type, T>; }
template <typename Obj> void exercise(Obj&& obj) {
std::cout << "===== " << __PRETTY_FUNCTION__ << "\n";
if constexpr(has_bar(obj)) {
std::cout << "called T bar() -> something or void\n";
obj.bar();
}
if constexpr(converts<int>(has_foo(obj))) {
std::cout << "called int foo() const -> " << obj.foo() << "\n";
}
if constexpr(converts<long>(has_foo(obj, "some text"))) {
std::cout << "called long foo(std::string) -> " << obj.foo("some text") << "\n";
}
}
int main() {
Everything e;
Everything const ce;
Some s;
Some const cs;
exercise(s);
exercise(cs);
exercise(ce);
exercise(e);
}
Prints
===== void exercise(Obj&&) [with Obj = Some&]
called int foo() const -> -2
===== void exercise(Obj&&) [with Obj = const Some&]
called int foo() const -> -2
===== void exercise(Obj&&) [with Obj = const Everything&]
called int foo() const -> -1
===== void exercise(Obj&&) [with Obj = Everything&]
called T bar() -> something or void
called int foo() const -> -1
called long foo(std::string) -> 42
OK. I have taken Alan Birtles advice and had a look at C++20 concepts for the solution.
Perhaps the use of std::addressof is overkill but it makes it almost a one-liner without a macro to define a HasMethodXYZ concept which may then be used for if constexpr or for easy SFINAE via a constraint. For example:
template <typename T>
concept HasMethodEvent = requires(T a, void (T::*m)(const std::string&) const) {
{&a == std::addressof(a)};
{m = &T::event};
};
struct dude_noway {};
struct dude_no {
void event(std::string& f) const {}
};
struct dude_yes {
void event(const std::string& f) const {}
};
template <typename T>
bool perhaps_event() {
if constexpr (HasMethodEvent<T>) {
return true;
} else {
return false;
}
}
template <HasMethodEvent T>
bool perhaps_event_sfinae() {
return true;
}
template <typename T>
bool perhaps_event_sfinae() {
return false;
}
//Catch2 test-case check
TEST_CASE("simple event check", "[check_method]") {
REQUIRE(perhaps_event<dude_yes>());
REQUIRE_FALSE(perhaps_event<dude_no>());
REQUIRE_FALSE(perhaps_event<dude_noway>());
REQUIRE(perhaps_event_sfinae<dude_yes>());
REQUIRE_FALSE(perhaps_event_sfinae<dude_no>());
REQUIRE_FALSE(perhaps_event_sfinae<dude_noway>());
}
which works OK with clang++-10 using libstdc++-10. For the win, I prefer this to the Boost TTI approach as it co-locates the method signature with the method name as part of the concept rather than using the MPL vector later and it feels simpler.
Thanks, --Matt.
I've looking for how to cast class member to C-style callback.
Recentrly i found answer with special bind hack allows to bind class members to C-style callbacks:
https://stackoverflow.com/a/39524069/5405443
I have this working code to bind function MyClass::f to C function f:
But i want to avoid explicit passing cb_type as template parameter to c_bind function.
In provided example CB has type void (*)(int) and Func template parameter has void (MyClass::*)(int) type.
template<typename CB, typename Func, typename... Params>
CB* c_bind(std::_Bind<Func(Params...)> function) {
return Callback<typename ActualType<CB>::type, __COUNTER__, Func>::getCallback(function);
}
typedef void (cb_type)(int);
class MyClass {
public:
void f(int x) {
std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
}
};
int main() {
MyClass mc;
auto f = c_bind<cb_type>(std::bind(&MyClass::f, mc, std::placeholders::_1));
// ^ how to avoid explicit callback type declaration here?
f(10);
return 0;
}
Also i found this piece of code (https://gist.github.com/vikchopde/73b62314379f733e8938f11b246df49c) for "unwrapping" some kind of functions.
bool ok = fu::is_unwrappable<decltype(&MyClass::f)>::value; // always false
// fu::unwrap_function<decltype(&MyClass::f)>::type::function_ptr blah; // won't compile
but it won't work by unknown to me reason.
My question is there any workaround to extract return type and args list from type with class-memeber pointer like void (MyClass::*)(int) and contruct C-like type void (*)(int) ?
Thank you for any help!
Well, in C++17, you are allowed to pass an arbitrary non-type parameter to a class with template<auto>. Therefore, we could store MyClass::f as a template parameter and parse its type with decltype. After passing this type to another templated class, we are able to extract desired types using template specialization.
The code below shows how to construct a C-style function wrapper<>::func_type.
Since you seem to bind an object to its member function, I additionally write the demo code to do this by invoking wrapper<>::bind. Hope it helps.
class MyClass {
public:
void f(int x) {
std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
}
};
void f(int x) {
std::cout << "Hello from f(int), value: " << x << std::endl;
}
template<auto F>
struct wrapper
{
template<typename> struct inner;
template<class Cls, typename Ret, typename... Args>
struct inner<Ret(Cls::*)(Args...)>
{
using func_type = Ret(Args...);
static auto bind(Cls *obj)
{
return [=](Args ...args){
return (obj->*F)(std::forward<Args>(args)...);
};
}
};
using func_type = typename inner<decltype(F)>::func_type;
static const constexpr auto bind = inner<decltype(F)>::bind;
};
int main() {
MyClass mc;
auto h = wrapper<&MyClass::f>::bind(&mc);
h(10);
using func_t = typename wrapper<&MyClass::f>::func_type;
std::function<func_t> g = f;
g(1);
return 0;
}
First of all i would like to thank #Dappur for nice example. Using your guide i will rewrite my ugly bind interface with std::_Bind usage later. Also i want to thank #Sam Varshavchik for mentioning that set of C++ books. I'll start reading it to become C++ grandmaster like you to learn how "why i cannot cast it like this". But unfortunately with my poor c++ experience I can still do it now. Here is working code:
template<class T, unsigned int n, class CallerType>
struct CallbackWrapper;
template<class Ret, class... Params, unsigned int n, class CallerType>
struct CallbackWrapper<Ret(Params...), n, CallerType> {
static auto get(std::function<Ret(Params...)>&& fn) -> Ret(*)(Params...) {
func = fn;
return static_cast<Ret(*)(Params...)>(CallbackWrapper<Ret(Params...), n, CallerType>::callback);
}
private:
static std::function<Ret(Params...)> func;
static Ret callback(Params... args) {
return func(args...);
}
};
template<class Ret, class... Params, unsigned int n, class CallerType>
std::function<Ret(Params...)> CallbackWrapper<Ret(Params...), n, CallerType>::func;
template<typename T>
struct lambda_to_stdf {
using type = void;
};
template<typename Ret, typename Class, typename... Args>
struct lambda_to_stdf<Ret(Class::*)(Args...) const> {
using type = std::function<Ret(Args...)>;
};
template<class Ret, class Cls, class... Args1, class... Args2>
auto c_bind(std::_Bind<Ret(Cls::*(Cls, Args1...))(Args2...)> function) -> Ret(*)(Args2...) {
return CallbackWrapper<Ret(Args2...), __COUNTER__, Ret(Cls::*(Cls, Args1...))(Args2...)>::get(std::move(function));
}
template<class Ret, class... Args>
auto c_bind(std::function<Ret(Args...)> function) -> Ret(*)(Args...) {
return CallbackWrapper<Ret(Args...), __COUNTER__, std::function<Ret(Args...)>>::get(std::move(function));
}
template<class F>
auto c_bind(F function) -> decltype(c_bind((typename lambda_to_stdf<decltype(&F::operator())>::type)(function))) {
return c_bind((typename lambda_to_stdf<decltype(&F::operator())>::type)(function));
}
Usage:
class MyClass {
public:
void f(int x) {
std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
}
};
int main() {
MyClass mc;
auto f = c_bind(std::bind(&MyClass::f, mc, std::placeholders::_1));
f(10);
std::function<void(int)> stdf = [](int v) {
std::cout << "hello from std::function, value: " << v << std::endl;
};
auto f2 = c_bind(stdf);
f2(100);
auto f3 = c_bind([](int v) -> int {
std::cout << "hello from lambda, value: " << v << std::endl;
return 5.0f;
});
f3(1000);
return 0;
}
Hope it will be helpful for someone.
I am trying to implement a resource protection class which would combine data along with a shared mutex (actually, QReadWriteLock, but it's similar). The class must provide the method to apply a user-defined function to the data when the lock is acquired. I would like this apply method to work differently depending on the function parameter (reference, const reference, or value). For example, when the user passes a function like int (const DataType &) it shouldn't block exclusively as we are just reading the data and, conversely, when the function has the signature like void (DataType &) that implies data modification, hence the exclusive lock is needed.
My first attempt was to use std::function:
template <typename T>
class Resource1
{
public:
template <typename Result>
Result apply(std::function<Result(T &)> &&f)
{
QWriteLocker locker(&this->lock); // acquire exclusive lock
return std::forward<std::function<Result(T &)>>(f)(this->data);
}
template <typename Result>
Result apply(std::function<Result(const T &)> &&f) const
{
QReadLocker locker(&this->lock); // acquire shared lock
return std::forward<std::function<Result (const T &)>>(f)(this->data);
}
private:
T data;
mutable QReadWriteLock lock;
};
But std::function doesn't seem to restrict parameter constness, so std::function<void (int &)> can easily accept void (const int &), which is not what I want. Also in this case it can't deduce lambda's result type, so I have to specify it manually:
Resource1<QList<int>> resource1;
resource1.apply<void>([](QList<int> &lst) { lst.append(11); }); // calls non-const version (ok)
resource1.apply<int>([](const QList<int> &lst) -> int { return lst.size(); }); // also calls non-const version (wrong)
My second attempt was to use std::result_of and return type SFINAE:
template <typename T>
class Resource2
{
public:
template <typename F>
typename std::result_of<F (T &)>::type apply(F &&f)
{
QWriteLocker locker(&this->lock); // lock exclusively
return std::forward<F>(f)(this->data);
}
template <typename F>
typename std::result_of<F (const T &)>::type apply(F &&f) const
{
QReadLocker locker(&this->lock); // lock non-exclusively
return std::forward<F>(f)(this->data);
}
private:
T data;
mutable QReadWriteLock lock;
};
Resource2<QList<int>> resource2;
resource2.apply([](QList<int> &lst) {lst.append(12); }); // calls non-const version (ok)
resource2.apply([](const QList<int> &lst) { return lst.size(); }); // also calls non-const version (wrong)
Mainly the same thing happens: as long as the object is non-const the mutable version of apply gets called and result_of doesn't restrict anything.
Is there any way to achieve this?
You may do the following
template <std::size_t N>
struct overload_priority : overload_priority<N - 1> {};
template <> struct overload_priority<0> {};
using low_priority = overload_priority<0>;
using high_priority = overload_priority<1>;
template <typename T>
class Resource
{
public:
template <typename F>
auto apply(F&& f) const
// -> decltype(apply_impl(std::forward<F>(f), high_priority{}))
{
return apply_impl(std::forward<F>(f), high_priority{});
}
template <typename F>
auto apply(F&& f)
// -> decltype(apply_impl(std::forward<F>(f), high_priority{}))
{
return apply_impl(std::forward<F>(f), high_priority{});
}
private:
template <typename F>
auto apply_impl(F&& f, low_priority) -> decltype(f(std::declval<T&>()))
{
std::cout << "ReadLock\n";
return std::forward<F>(f)(this->data);
}
template <typename F>
auto apply_impl(F&& f, high_priority) -> decltype(f(std::declval<const T&>())) const
{
std::cout << "WriteLock\n";
return std::forward<F>(f)(this->data);
}
private:
T data;
};
Demo
Jarod has given a workaround, but I'll explain why you cannot achieve that this regular way.
The problem is that:
Overload resolution prefers non-const member functions over const member functions when called from a non-const object
whatever object this signature void foo(A&) can accept, void foo(const A&) can also the same object. The latter even has a broader binding set than the former.
Hence, to solve it, you will have to at least defeat point 1 before getting to 2. As Jarod has done.
From your signatures (see my comment annotations):
template <typename F>
typename std::result_of<F (T &)>::type apply(F &&f) //non-const member function
{
return std::forward<F>(f)(this->data);
}
template <typename F>
typename std::result_of<F (const T &)>::type apply(F &&f) const //const member function
{
return std::forward<F>(f)(this->data);
}
When you call it like:
resource2.apply([](QList<int> &lst) {lst.append(12); }); //1
resource2.apply([](const QList<int> &lst) { return lst.size(); }); //2
First of all, remember that resource2 isn't a const reference. Hence, the non-const membr function of apply will always be prefered by Overload resolution.
Now, taking the case of the first call //1, Whatever that lambda is callable with, then then the second one is also callable with that object
A simplified mock-up of what you are trying to do is:
struct A{
template<typename Func>
void foo(Func&& f); //enable if we can call f(B&);
template<typename Func>
void foo(Func&& f) const; //enable if we can call f(const B&);
};
void bar1(B&);
void bar2(const B&);
int main(){
A a;
a.foo(bar1);
a.foo(bar2);
//bar1 and bar2 can be both called with lvalues
B b;
bar1(b);
bar2(b);
}
As I understand it, you want to discriminate a parameter that's a std::function that takes a const reference versus a non-constant reference.
The following SFINAE-based approach seems to work, using a helper specialization class:
#include <functional>
#include <iostream>
template<typename ...Args>
using void_t=void;
template<typename Result,
typename T,
typename lambda,
typename void_t=void> class apply_helper;
template <typename T>
class Resource1
{
public:
template <typename Result, typename lambda>
Result apply(lambda &&l)
{
return apply_helper<Result, T, lambda>::helper(std::forward<lambda>(l));
}
};
template<typename Result, typename T, typename lambda, typename void_t>
class apply_helper {
public:
static Result helper(lambda &&l)
{
std::cout << "T &" << std::endl;
T t;
return l(t);
}
};
template<typename Result, typename T, typename lambda>
class apply_helper<Result, T, lambda,
void_t<decltype( std::declval<lambda>()( std::declval<T>()))>> {
public:
static Result helper(lambda &&l)
{
std::cout << "const T &" << std::endl;
return l( T());
}
};
Resource1<int> test;
int main()
{
auto lambda1=std::function<char (const int &)>([](const int &i)
{
return (char)i;
});
auto lambda2=std::function<char (int &)>([](int &i)
{
return (char)i;
});
auto lambda3=[](const int &i) { return (char)i; };
auto lambda4=[](int &i) { return (char)i; };
test.apply<char>(lambda1);
test.apply<char>(lambda2);
test.apply<char>(lambda3);
test.apply<char>(lambda4);
}
Output:
const T &
T &
const T &
T &
Demo
The helper() static class in the specialized class can now be modified to take a this parameter, instead, and then use it to trampoline back into the original template's class's method.
As long as the capture lists of your lambdas are empty, you can rely on the fact that such a lambda decays to a function pointer.
It's suffice to discriminate between the two types.
It follows a minimal, working example:
#include<iostream>
template <typename T>
class Resource {
public:
template <typename Result>
Result apply(Result(*f)(T &)) {
std::cout << "non-const" << std::endl;
return f(this->data);
}
template <typename Result>
Result apply(Result(*f)(const T &)) const {
std::cout << "const" << std::endl;
return f(this->data);
}
private:
T data;
};
int main() {
Resource<int> resource;
resource.apply<void>([](int &lst) { });
resource.apply<int>([](const int &lst) -> int { return 42; });
}
I have a function definition like so
template <typename T>
auto print(T t) -> decltype(t.print()) {
return t.print();
}
The idea is that the argument must be of type T and must have the print function. This print function could return anything, explaining the need of decltype. So for example you can do:
struct Foo
{
int print()
{
return 42;
}
};
struct Bar
{
std::string print()
{
return "The answer...";
}
};
...
std::cout << print(Foo()) << std::endl;
std::cout << print(Bar()) << std::endl;
/* outputs:
42
The answer...
*/
I read that templates cannot do runtime instantiation and that you can have the classes derive from a base class, then determine their types to see what template argument to use. However, how would I do this for a non-class type? The idea is to be able to have:
template <typename T>
T print(T t) {
return t;
}
as well, but this gives me ambiguous overload errors. Qualifying doesn't work, ie print<Foo>. And the other got'cha is, what if I had a functor like:
struct Foo
{
virtual int print();
operator int() const
{
return 42;
}
};
How does it decide now?
So my question is, is it possible to resolve all these ambiguities with templates, or do I have to write a bunch of redundant code?
Tests
I incrementally added tests, copy/pasting each edit'ed solution from below. Here are the results:
With the following classes:
struct Foo
{
int print()
{
return 42;
}
operator int() const
{
return 32;
}
};
struct Bar
{
std::string print()
{
return "The answer...";
}
operator int() const
{
return (int)Foo();
}
};
struct Baz
{
operator std::string() const
{
return std::string("The answer...");
}
};
And the following test output:
std::cout << print(Foo()) << std::endl;
std::cout << print(Bar()) << std::endl;
std::cout << print(42) << std::endl;
std::cout << print((int)Foo()) << std::endl;
std::cout << print("The answer...") << std::endl;
std::cout << print(std::string("The answer...")) << std::endl;
std::cout << print((int)Bar()) << std::endl;
std::cout << print((std::string)Baz()) << std::endl;
Both correctly output:
42
The answer...
42
32
The answer...
The answer...
32
The answer...
You could adopt the following approach, which invokes a print() member function on the input if such a member function exists, otherwise it will return the input itself:
namespace detail
{
template<typename T, typename = void>
struct print_helper
{
static T print(T t) {
return t;
}
};
template<typename T>
struct print_helper<T, decltype(std::declval<T>().print(), (void)0)>
{
static auto print(T t) -> decltype(t.print()) {
return t.print();
}
};
}
template<typename T>
auto print(T t) -> decltype(detail::print_helper<T>::print(t))
{
return detail::print_helper<T>::print(t);
}
Here is a live example.
Simple solution using manual overloading for each type which you want to print directly:
Define your first implementation which calls T::print(). Use overloading to specify alternative implementations for all types which don't have this function. I don't recommend this solution, but it's very easy to understand.
template<typename T>
auto print(T t) -> decltype(t.print()) {
return t.print();
}
int print(int t) {
return t;
}
std::string print(std::string t) {
return t;
}
// ... and so on, for each type you want to support ...
More advanced solution using SFINAE which uses T::print() automatically if and only if it's there:
First, define a trait which can decide if your type has a function print(). Basically, this trait inherits from either std::true_type or std::false_type, depending on the decision being made in some helper class (_test_print). Then, use this type trait in an enable_if compile-time decision which defines only one of the two cases and hides the other one (so this is not overloading).
// Type trait "has_print" which checks if T::print() is available:
struct _test_print {
template<class T> static auto test(T* p) -> decltype(p->print(), std::true_type());
template<class> static auto test(...) -> std::false_type;
};
template<class T> struct has_print : public decltype(_test_print::test<T>(0)) {};
// Definition of print(T) if T has T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<has_print<T>::value, decltype(t.print())>::type {
return t.print();
}
// Definition of print(T) if T doesn't have T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<!has_print<T>::value, T>::type {
return t;
}
Have a look at the live demo.