I'm trying to do a templated user-defined-conversion that uses design by introspection.
In C++20 I can do the following:
template<typename T>
operator T() const
{
if constexpr( requires { PFMeta<T>::from_cursor(*this); } )
{
return PFMeta<T>::from_cursor(*this);
}
else
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
}
i.e. if PFMeta<T>::from_cursor(const struct PFCursor&) is defined then use it else do a memcpy. (longer example here in godbolt: https://godbolt.org/z/abed1vsK3)
I love this approach but unfortunately this library will need to work on C++17 too.
So I've been trying SFINAE as an alternative to the concepts but it's very tricky.
I finally managed to get something similar but with a templated method as rather than the user-defined-conversion operator itself:
template<typename T, typename = void>
struct has_from_cursor : std::false_type { };
template<typename T>
struct has_from_cursor<T, decltype( PFMeta<T>::from_cursor(std::declval<struct PFCursor>()), void() ) > : std::true_type { };
// ...
template<class T>
std::enable_if_t<has_from_cursor<T>::value, T> as() const
{
return PFMeta<T>::from_cursor(*this);
}
template<class T>
std::enable_if_t<!has_from_cursor<T>::value, T> as() const
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
I tried the following which compiles but does not work (I can't cast with it):
template<typename T>
operator std::enable_if_t<has_from_cursor<T>::value, T>() const
{
return PFMeta<T>::from_cursor(*this);
}
template<typename T>
operator std::enable_if_t<!has_from_cursor<T>::value, T>() const
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
Longer example here: https://godbolt.org/z/5r9Mbo18h
So two questions:
Can I do effectively what I can do with concepts with just SFINAE in C++17 for the user defined conversion operator?
Is there a simpler way to do the as approach than the type traits & enable_if? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).
Thanks!
Can I do effectively what I can do with concepts with just SFINAE in C++17 for the user defined conversion operator?
Consider that C++17 support if constepr. Given that you've developed a has_from_cursor custom type traits that inherit from std::true_type or from std::false_type, you can use it for if constexp.
I mean (caution: code not tested)
template<typename T>
operator T() const
{ // .............VVVVVVVVVVVVVVVVVVVVVVVVV
if constexpr( has_from_cursos<T>::value )
{
return PFMeta<T>::from_cursor(*this);
}
else
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
}
Is there a simpler way to do the as approach than the type traits & enable_if? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).
I suppose you can try tag-dispatching...
I mean something as calling 'as()', from operator T() with an additional int argument
template<typename T>
operator T() const
{ return as<T>(0); } // <-- call as() with a int
where there is a as() specific for from_cursos class enabled, that receive an unused int and is simply SFINAE enabled/disabled through decltype()
template <typename T> // ........accept an int; best match
decltype(PFMeta<T>::from_cursor(*this)) as (int) const
{ return PFMeta<T>::from_cursor(*this); }
and a generic as(), receiving a long
template <typename T>
T as (long) const // <-- accept a long; worst match
{
T x;
std::memcpy(&x, &data, sizeof(x));
return x;
}
The trick is the unused argument: a int.
When the specialized as() is enabled, if preferred because accept a int so is a better match.
When the specialized as() is disabled, remain the generic as() as better-than-nothig match.
Related
I am writing a kind of container class, for which I would like to offer an apply method which evaluates a function on the content of the container.
template<typename T>
struct Foo
{
T val;
/** apply a free function */
template<typename U> Foo<U> apply(U(*fun)(const T&))
{
return Foo<U>(fun(val));
}
/** apply a member function */
template<typename U> Foo<U> apply(U (T::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
struct Bar{};
template class Foo<Bar>; // this compiles
//template class Foo<int>; // this produces an error
The last line yields error: creating pointer to member function of non-class type ‘const int’. Even though I only instantiated Foo and not used apply at all. So my question is: How can I effectively remove the second overload whenever T is a non-class type?
Note: I also tried having only one overload taking a std::function<U(const T&)>. This kinda works, because both function-pointers and member-function-pointers can be converted to std::function, but this approach effectively disables template deduction for U which makes user-code less readable.
Using std::invoke instead helps, it is much easier to implement and read
template<typename T>
struct Foo
{
T val;
template<typename U> auto apply(U&& fun)
{
return Foo<std::invoke_result_t<U, T>>{std::invoke(std::forward<U>(fun), val)};
}
};
struct Bar{};
template class Foo<Bar>;
template class Foo<int>;
However, this won't compile if the functions are overloaded
int f();
double f(const Bar&);
Foo<Bar>{}.apply(f); // Doesn't compile
The way around that is to use functors instead
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
Which also makes it more consistent with member function calls
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
In order to remove the second overload you'd need to make it a template and let SFINAE work, e. g. like this:
template<typename T>
struct Foo
{
T val;
//...
/** apply a member function */
template<typename U, typename ObjT>
Foo<U> apply(U (ObjT::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
Alternatively, you could remove the second overload altogether, and use lambda or std::bind:
#include <functional> // for std::bind
template<typename T>
struct Foo
{
T val;
/** apply a member function */
template<typename U, typename FuncT>
Foo<U> apply(FuncT&& f)
{
return {f(val)};
}
};
struct SomeType
{
int getFive() { return 5; }
};
int main()
{
Foo<SomeType> obj;
obj.apply<int>(std::bind(&SomeType::getFive, std::placeholders::_1));
obj.apply<int>([](SomeType& obj) { return obj.getFive(); });
}
How can I effectively remove the second overload whenever T is a non-class type?
If you can use at least C++11 (and if you tried std::function I suppose you can use it), you can use SFINAE with std::enable_if
template <typename U, typename V>
typename std::enable_if<std::is_class<V>{}
&& std::is_same<V, T>{}, Foo<U>>::type
apply(U (V::*fun)() const)
{ return Foo<U>((val.*fun)()); }
to impose that T is a class.
Observe that you can't check directly T, that is a template parameter of the class, but you have to pass through a V type, a template type of the specific method.
But you can also impose that T and V are the same type (&& std::is_same<V, T>{}).
Given a template class A with a single template argument T, is it possible to only overload operators in A which are available for type T? For example:
template <typename T>
class A
{
public:
#if hasOperator(T, +=)
T& operator +=(const T &rhs)
{
mValue += rhs;
return mValue;
}
#endif
private:
T mValue;
}
int main()
{
A<int> a;
a += 8; //+= will forward to the += for the int
struct Test { /*no operators defined*/ };
A<Test> b; //+= is not implemented since Test does not implement +=
}
I'm writting a generic wrapper class that needs to behave exactly like the template type. So if T has operator +=, A will (at compile time) overload += accordingly. Yes, I could go ahead and just implement every operator in A, but then the compiler will error when T doesn't have a certain operator. At first I though template specialization might be the answer, but that would require a specialization for every type. While this could work and be a lot of typing, it wont because A needs to work with any type (not just what is specialized).
Use expression SFINAE to drop your operator+ from the overload resolution set unless T defines operator+
template <typename T>
class A
{
private:
T mValue;
public:
template<typename U=T>
auto operator +=(const U &rhs)
-> decltype(mValue += rhs)
{
mValue += rhs;
return mValue;
}
};
Live demo
I will give 3 solutions in decreasing complexity and utility. The last solution is the simplest, and least complex.
A small, if useful, metaprogramming library:
template<class...>struct types{using type=types;};
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,std::void_t<Z<Ts...>>> :
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;
A trait for the result of +=:
template<class Lhs, class Rhs>
using plus_equal_result = decltype(std::declval<Lhs>()+=std::declval<Rhs>());
template<class Lhs, class Rhs>
using can_plus_equal = can_apply< plus_equal_result, Lhs, Rhs >;
template<class T>
using can_self_plus_equal = can_plus_equal< T&, T const& >;
which gives us some nice traits that return true type or false type depending on if += is valid.
template<class A, class T, bool b = can_self_plus_equal<T>{}>
struct A_maybe_plus_equal {};
template<class A, class T>
struct A_maybe_plus_equal<A, T, true> {
A& self() { return *static_cast<A*>(this); }
A& operator+=( T && t )
{
self().mResult += std::move(t);
return self();
}
template<class U>
std::enable_if_t<can_plus_equal<T&,U>{},A&> operator+=( U && u )
{
self().mResult += std::forward<U>(u);
return self();
}
};
which gives us a += iff we pass true.
template <class T>
class A:
public A_maybe_plus_equal<A<T>, T>
{
friend class A_maybe_plus_equal<A<T>, T>;
public:
// nothing needed
private:
T mValue;
};
which gives you a += overload that takes a const T& or a T&& or a U&& on the right hand side if and only if T& += T const& is a valid expression.
This is the "perfect" solution, but it is complex.
Note that each operator can be done separately, so you don't have a combinatorial explosion of specializations.
Now, the there is an easier option. It has the downside that it doesn't support {} based construction on the right hand side, and under some readings of the standard it is illegal.
It is, however, still SFINAE friendly:
template <typename T>
class A {
public:
template<class U>
auto operator +=(U&&rhs)
-> decltype( (std::declval<T&>()+=std::declval<U&&>()),void(),A& )
// or std::enable_if_t<can_plus_equal<T&,U>{},A&>
{
mValue += std::forward<U>(rhs);
return *this;
}
private:
T mValue;
};
This can be folded into the above option, and give both {} and perfect forwarding syntax. I find that the T const& can be dropped if you have a template perfect forwarder.
The reason why this is technically undefined behavior is that the standard mandates that all template functions have at least one set of arguments that would render their body able to compile. In a given class instance, the template += of the above may have no such set of type arguments, which makes your program ill-formed with no diagnostic required (ie, UB).
There is another rule that member functions of template classes don't get instantiated unless called. Some argue that this other rule supersedes the one I mentioned in the last paragraph.
Another argument is that the method may be legal so long as there is some mixture of template arguments to the enclosing class(es) and to the template method itself that lead to it being instantiatable. I'd guess that this is what the standard committee intended, but I don't know how to read the standard to get this result.
This argument also applies to the plus_equal function in answer #1. That implementation need not be as simple. In addition, #1 provides {} based += syntax, which is a practical reason to use it. This concern -- that the program is technically ill-formed -- is academic, as all compilers I have used have no problems with this construct.
The paragraph three above this one gives us our final option. Do nothing.
template <typename T>
class A {
public:
A& operator +=(const T &rhs) {
mValue += rhs;
return *this;
}
private:
T mValue;
};
which means that you cannot SFINAE test that += doesn't work, but so long as you don't call += it "works". This is how vector's operator< works, for example. This is a lower "quality" solution, and cases of this in the standard library tend to be repaired over time.
However, as a first pass, this last choice is usually best. Only if you expect SFINAE requirements are the above hoops worthwhile.
Ultimately, C++1z is introducing concepts. I believe concepts will make this problem much easier, as eliminating overloads from consideration based on the type arguments of the enclosing class is a perennial problem in std.
You don't actually have to do anything. Individual member functions of a template class won't get instantiatiated until use. You say:
but then the compiler will error when T doesn't have a certain operator.
But isn't that clearer than having it error when A<T> doesn't? If you have:
template <typename T>
class A
{
public:
A& operator +=(const T &rhs)
{
mValue += rhs;
return *this;
}
A& operator-=(const T &rhs)
{
mValue -= rhs;
return *this;
}
// etc
private:
T mValue;
};
Then this will just work:
int main() {
A<int> a;
a += 8; //+= will forward to the += for the int
struct Test {
Test& operator-=(const Test& ) { return *this; }
};
A<Test> b;
b -= Test{}; // totally fine
b += Test{}; // error: no match for +=
// (operand types are 'main()::Test' and 'const main()::Test')
}
I'm creating a very small C++ project, and I'd like to create a simple vector class for my own needs. The std::vector template class will not do. When the vector class is comprised of chars (i.e. vector<char>), I'd like it to be able to be compared to a std::string. After a bit of messing around, I wrote code that both compiles and does what I want. See below:
#include <string>
#include <stdlib.h>
#include <string.h>
template <typename ElementType>
class WorkingSimpleVector {
public:
const ElementType * elements_;
size_t count_;
// ...
template <typename ET = ElementType>
inline typename std::enable_if<std::is_same<ET, char>::value && std::is_same<ElementType, char>::value, bool>::type
operator==(const std::string & other) const {
if (count_ == other.length())
{
return memcmp(elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
template <typename ElementType>
class NotWorkingSimpleVector {
public:
const ElementType * elements_;
size_t count_;
// ...
inline typename std::enable_if<std::is_same<ElementType, char>::value, bool>::type
operator==(const std::string & other) const {
if (count_ == other.length())
{
return memcmp(elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
int main(int argc, char ** argv) {
// All of the following declarations are legal.
WorkingSimpleVector<char> wsv;
NotWorkingSimpleVector<char> nwsv;
WorkingSimpleVector<int> wsv2;
std::string s("abc");
// But this one fails: error: no type named ‘type’ in ‘struct std::enable_if<false, bool>’
NotWorkingSimpleVector<int> nwsv2;
(wsv == s); // LEGAL (wanted behaviour)
(nwsv == s); // LEGAL (wanted behaviour)
// (wsv2 == s); // ILLEGAL (wanted behaviour)
// (nwsv2 == s); // ??? (unwanted behaviour)
}
I believe I understand why the error is occurring: The compiler creates the class definition for NotWorkingSimpleVector<int>, and then the return type of my operator== function becomes:
std::enable_if<std::is_same<int, char>::value, bool>::type
which then becomes:
std::enable_if<false, bool>::type
which then yields an error: there is no type member of std::enable_if<false, bool>, which is indeed the entire point of the enable_if template.
I have two questions.
Why won't SFINAE simply disable the definition of operator== for NotWorkingSimpleVector<int>, like I want it to? Is there some compatibility reason for this? Are there other use-cases I'm missing; does a reasonable counter-argument for this behaviour exist?
Why does the first class (WorkingSimpleVector) work? It seems to me that the compiler 'reserves judgement': Since the 'ET' parameter is not yet defined, it gives up trying to tell if operator== can exist. Are we relying on the compilers 'lack of insight' to allow this kind of conditionally enabled function (even if this 'lack of insight' is acceptable by the C++ specification)?
SFINAE works in a template function. In the context of template type substitution, substitution failure in the immediate context of the substitution is not an error, and instead counts as substitution failure.
Note, however, that there must be a valid substitution or your program is ill formed, no diagnostic required. I presume this condition exists in order that further "more intrusive" or complete checks can be added to the language in the future that check the validity of a template function. So long as said checks are actually checking that the template could be instantiated with some type, it becomes a valid check, but it could break code that expects that a template with no valid substitutions is valid, if that makes sense. This could make your original solution an ill formed program if there is no template type you could pass to the operator== function that would let the program compile.
In the second case, there is no substitution context, so SFINAE does not apply. There is no substitution to fail.
Last I looked at the incoming Concepts proposal, you could add requires clauses to methods in a template object that depend on the template parameters of the object, and on failure the method would not be considered for overload resolution. This is in effect what you want.
There is no standards-compliant way to do this under the current standard. The first attempt is one that may people commonly do, and it does compile, but it is technically in violation of the standard (but no diagnostic of the failure is required).
The standards compliant ways I have figured out to do what you want:
Changing one of the parameters of the method to be a reference to a never-completed type if your condition fails. The body of the method is never instantiate if not called, and this technique prevents it from being called.
Using a CRTP base class helper that uses SFINAE to include/exclude the method depending on an arbitrary condition.
template <class D, class ET, class=void>
struct equal_string_helper {};
template <class D, class ET>
struct equal_string_helper<D,ET,typename std::enable_if<std::is_same<ET, char>::value>::type> {
D const* self() const { return static_cast<D const*>(this); }
bool operator==(const std::string & other) const {
if (self()->count_ == other.length())
{
return memcmp(self()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
where we do this:
template <typename ElementType>
class WorkingSimpleVector:equal_string_helper<WorkingSimpleVector,ElementType>
We can refactor the conditional machinery out of the CRTP implementation if we choose:
template<bool, template<class...>class X, class...>
struct conditional_apply_t {
struct type {};
};
template<template<class...>class X, class...Ts>
struct conditional_apply_t<true, X, Ts...> {
using type = X<Ts...>;
};
template<bool test, template<class...>class X, class...Ts>
using conditional_apply=typename conditional_apply_t<test, X, Ts...>::type;
Then we split out the CRTP implementation without the conditional code:
template <class D>
struct equal_string_helper_t {
D const* self() const { return static_cast<D const*>(this); }
bool operator==(const std::string & other) const {
if (self()->count_ == other.length())
{
return memcmp(self()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
then hook them up:
template<class D, class ET>
using equal_string_helper=conditional_apply<std::is_same<ET,char>::value, equal_string_helper_t, D>;
and we use it:
template <typename ElementType>
class WorkingSimpleVector: equal_string_helper<WorkingSimpleVector<ElementType>,ElementType>
which looks identical at point of use. But the machinery behind was refactored, so, bonus?
Templating operator== basically makes it uncallable. You'd have to explicitly do:
myvec.operator==<char>(str);
The simplest solution might just to add a non-member function:
bool operator==(const WorkingVector<char>& vec, const std::string& s);
bool operator==(const std::string& s, const WorkingVector<char>& vec);
To enable SFINAE and to keep it as a member function, you'd have to forward your type to something else:
bool operator==(const std::string& s) const {
return is_equal<ElementType>(s);
}
template <typename T> // sure, T == ET, but you need it in this context
// in order for SFINAE to apply
typename std::enable_if<std::is_same<T, char>::value, bool>::type
is_equal(const std::string& s) {
// stuff
}
Simple answer
This answer bares parallels to Yakk's answer but is not quite as useful (Yakk's one supports arbitrary if-expressions). It is, however, quite a bit simpler and easier to understand.
template <typename ThisClass, typename ElementType>
class WorkingSimpleVector_Base {
};
template <typename ThisClass>
class WorkingSimpleVector_Base<ThisClass, char> {
private:
ThisClass * me() { return static_cast<ThisClass*>(this); };
const ThisClass * me() const { return static_cast<const ThisClass*>(this); };
public:
bool operator==(const std::string & other) const {
if (me()->count_ == other.length())
{
return memcmp(me()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
template <typename ElementType>
class WorkingSimpleVector : public WorkingSimpleVector_Base<WorkingSimpleVector<ElementType>, ElementType> {
public:
const ElementType * elements_;
size_t count_;
};
This works by utilising template specialization for the 'if statements' we want. We base the class off of WorkingSimpleVector_Base, which then only contains functions if the ElementType value is char (second definition of WorkingSimpleVector_Base). Otherwise, it has no functions at all (first definition of WorkingSimpleVector_Base). The ThisClass parameter is what makes this a "CRTP" (Curiously Recurring Template Pattern). It allows the template to access the child class's fields through the use of the me() function. Remember that the template is no different from any other class so it won't have access to private members (unless the child class declares it as a friend).
Modified version of Yakk's answer explained
This first thing he/she declares is a helper template that does this whole conditional declaration for us:
template<bool, template<class...>class X, class...>
struct conditional_apply_t {
struct type {};
};
template<template<class...>class X, class...Ts>
struct conditional_apply_t<true, X, Ts...> {
using type = X<Ts...>;
};
template<bool test, template<class...>class X, class...Ts>
using conditional_apply=typename conditional_apply_t<test, X, Ts...>::type;
Variadic templates are scary, and I think they can be removed in this case. Let's simplify this to:
template<bool, class X>
struct conditional_apply_t {
struct type {};
};
template<class X>
struct conditional_apply_t<true, X> {
using type = X;
};
template<bool test, class X>
using conditional_apply=typename conditional_apply_t<test, X>::type;
conditional_apply_t's type type is an empty struct if the condition test is not true (see first definition of conditional_apply_t). If it is true, then the type type is the value of X. The definition of conditional_apply just removes the need for us to write ::type at the end of conditional_apply_t<...> every time we use this construct.
Next, we define a template that implements the behaviour we want
template <class D>
struct equal_string_helper_t {
D const* self() const { return static_cast<D const*>(this); }
bool operator==(const std::string & other) const {
if (self()->count_ == other.length())
{
return memcmp(self()->elements_, other.c_str(), other.length()) == 0;
}
return false;
}
};
In this case, the D parameter is what gives us the "CRTP" (Curiously Recurring Template Pattern). See the above "Simple Answer" for more details on why this is important.
Next, we declare a type that only has this operator== function if a condition is satisfied:
template<class D, class ET>
using equal_string_helper=conditional_apply<std::is_same<ET,char>::value, equal_string_helper_t<D>>;
So, the equal_string_helper<D,ET> type is:
An empty struct when ET != char
equal_string_helper_t<D> when ET == char
Finally, after all this, we can create the class we wanted with the following:
template <typename ElementType>
class WorkingSimpleVector : public equal_string_helper<WorkingSimpleVector<ElementType>, ElementType> {
public:
const ElementType * elements_;
size_t count_;
};
Which works as required.
I have a class named has_f and I want it to only accept template parameters that have a f member function. How would I do that? This is what I tried:
template <typename T, typename = void>
struct has_f : std::false_type {};
template <typename T>
struct has_f<
T,
typename = typename std::enable_if<
typename T::f
>::type
> : std::true_type {};
But I get some cryptic errors. Here is the class I want to use:
struct A
{
void f();
};
How do I do this correctly? Thanks.
From the title of your question I presume that you don't really need a type deriving from true_type or false_type - only to prevent compilation if method f is not present. If that is the case, and if you also require a specific signature (at least in terms of arguments) for that method, in C++11 you can do something like this:
template <typename T>
struct compile_if_has_f
{
static const size_t dummy = sizeof(
std::add_pointer< decltype(((T*)nullptr)->f()) >::type );
};
This is for the case when f() should not accept any arguments. std::add_pointer is only needed if f returns void, because sizeof(void) is illegal.
I +1ed rapptz yesterday for
"possible duplicate of
Check if a class has a member function of a given signature"
and haven't changed my mind.
I suppose it is arguable that this question unpacks to
"A) How to check if a class has a member function of a given signature and
B) How to insist that a class template argumement is a class
as per A)". To B) in this case I would answer with static_assert, since
the questioner apparently isn't interested in enable_if alternatives.
Here is a solution that adapts my answer to
"traits for testing whether func(args) is well-formed and has required return type"
This solution assumes that has_f<T>::value should be true if and only
if exactly the public member void T::f() exists, even if T overloads f or inherits f.
#include <type_traits>
template<typename T>
struct has_f
{
template<typename A>
static constexpr bool test(
decltype(std::declval<A>().f()) *prt) {
return std::is_same<void *,decltype(prt)>::value;
}
template <typename A>
static constexpr bool test(...) {
return false;
}
static const bool value = test<T>(static_cast<void *>(nullptr));
};
// Testing...
struct i_have_f
{
void f();
};
struct i_dont_have_f
{
void f(int);
};
struct i_also_dont_have_f
{
int f();
};
struct i_dont_quite_have_f
{
int f() const;
};
struct i_certainly_dont_have_f
{};
struct i_have_overloaded_f
{
void f();
void f(int);
};
struct i_have_inherited_f : i_have_f
{};
#include <iostream>
template<typename T>
struct must_have_f{
static_assert(has_f<T>::value,"T doesn't have f");
};
int main()
{
must_have_f<i_have_f> t0; (void)t0;
must_have_f<i_have_overloaded_f> t1; (void)t1;
must_have_f<i_have_inherited_f> t2; (void)t2;
must_have_f<i_dont_have_f> t3; (void)t3; // static_assert fails
must_have_f<i_also_dont_have_f> t4; (void)t4; // static_assert fails
must_have_f<i_dont_quite_have_f> t5; (void)t5; // static_assert fails
must_have_f<i_certainly_dont_have_f> t6; (void)t6; // static_assert fails
must_have_f<int> t7; (void)t7; // static_assert fails
return 0;
}
(Built with clang 3.2, gcc 4.7.2/4.8.1)
This toes a fine line between answering your question and providing a solution to your problem but not directly answering your question, but I think you may find this helpful.
For background, check out this question. The author mentions that he didn't like Boost's solution, and I didn't particularly like the one proposed there either. I was writing a quick & dirty serialization library (think python's marshal) where you would call serialize(object, ostream) on an object to serialize it. I realized I wanted this function call to one of four things:
If object is plain old data, just write out the size and raw data
If object is a class that I've created with its own member function (object::serialize), then call that member function
If there's a template specialization for that type, use it.
If none of the above is true, throw a compilation error; the serialize function is being used improperly.
When I code, I try to avoid stuff that is 'tricky' or hard to understand at a glance. I think this solution solves the same problem without using code that must be pondered for hours to understand:
#include <type_traits>
#include <iostream>
#include <vector>
#include <string>
// Template specialization for a POD object
template<typename T>
typename std::enable_if< std::is_pod<T>::value, bool>::type
serial(const T &out, std::ostream &os)
{
os.write((const char*) &out, sizeof(T));
return os.good();
}
// Non POD objects must have a member function 'serialize(std::ostream)'
template<typename T>
typename std::enable_if< ! std::is_pod<T>::value, bool>::type
serial(const T &out, std::ostream &os)
{
return out.serial(os);
}
// Additional specializations here for common container objects
template<typename T>
bool serial(const std::vector<T> &out, std::ostream &os)
{
const size_t vec_size = out.size();
if(!serial(vec_size, os))
return false;
for(size_t i =0; i < out.size(); ++i)
{
if(!serial(out[i], os))
return false;
}
return true;
}
class SomeClass
{
int something;
std::vector<double> some_numbers;
...
bool serial(std::ostream &os)
{
return serial(something, os) && serial(some_numbers, os);
}
};
If you can boil down your needs to a simple set of rules, and can live with a slightly less general solution, I think this method works well.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it possible to write a C++ template to check for a function's existence?
say there are 2 classes:
struct A{ int GetInt(){ return 10; } };
struct B{ int m; };
I want to use object of type A or B in following function
tempate< typename T >
int GetInt( const T & t )
{
//if it's A, I'll call: return t.GetInt();
//if its' B, I'll call: return t.m;
}
Now, because there are whole bunch of classes, some contain GetInt(), some don't, I don't want to write specialization for each type, I only want to distinguish them by 'containing GetInt() or not in compile time', how should I do this ?
Substitution Failure Is Not An Error, or more compactly, SFINAE
But in your particular case, you don't need SFINAE, virtual members, or anything fancy like that.
You just need an ordinary overloaded function.
int GetInt(A& t) { return t.GetInt(); }
int GetInt(const B& t) { return t.m; }
If there's code that needs to be shared between the different versions, refactor it so that there's a template that calls an overloaded inline function, all type-specific behavior is in the inline function, and all shared behavior is in the template.
For your "I have many many classes" need, SFINAE would look more or less like this:
template<typename T>
int GetInt(const T& t, int (T::*extra)() const = &T::GetInt)
{
return t.GetInt();
}
template<typename T>
auto GetInt(const T& t) -> decltype(t.m)
{
return t.m;
}
EDIT: The reality of SFINAE is much uglier, at least until C++0x comes around. In fact it starts looking just as bad as GMan's answer.
struct A{ int GetInt() const { return 10; } };
struct B{ int m; };
template<typename T, int (T::*extra)() const>
struct has_mfunc
{
typedef int type;
};
template<typename T>
typename has_mfunc<T, &T::GetInt>::type GetInt(const T& t)
{
return t.GetInt();
}
template<typename T, typename U, U (T::*extra)>
struct has_field
{
typedef U type;
};
template<typename T>
typename has_field<T, int, &T::m>::type GetInt(const T& t)
{
return t.m;
}
int main(void)
{
A a;
B b;
b.m = 5;
return GetInt(a) + GetInt(b);
}
Stealing from here, and assuming you fix your code so GetInt is const, we get:
HAS_MEM_FUNC(GetInt, has_GetInt);
template <bool B>
struct bool_type
{
static const bool value = B;
};
typedef bool_type<true> true_type;
typedef bool_type<false> false_type;
namespace detail
{
template <typename T>
int get_int(const T& pX, true_type)
{
return pX.GetInt();
}
template <typename T>
int get_int(const T& pX, false_type)
{
return pX.m;
}
}
template <typename T>
int get_int(const T& pX)
{
return detail::get_int(pX,
has_GetInt<T, int (T::*)() const>::value);
}
This is pretty awful design though. You should fix the problem rather than apply a patch.
Technically it just involves a few template arcana, which you can find by googling e.g. has_member or the like. Off the cuff, in the detection code, if I were to write such, I'd just sort of fake-derive from the class in question, and check size of derived class' member.
However, don't do that.
What else to do depends. But it seems like your classes conform to two different "schemas", so to speak, without those schemas being available via the type system (like, it seems the classes don't derive from two base classes A and B). Then one option is to introduce a traits template that tells you wrappers whether the template param T is schema A or B. Specialize the traits for each relevant class that differs from the default. Choose the default so as to minimize work.
Cheers & hth.,
This is exactly what inheritance is for. You can easily use use dynamic_cast for is type of questions at runtime. For example you can define an abstract base class called HasGetInt and derive the classes that need that function from it and not reinvent the wheel.