Consider this case with multiple (implementation) inheritance with mixin pattern:
#include <string>
#include <iostream>
template <typename... Bases>
struct Overloads : public Bases... {};
struct Human {};
struct Animal {};
struct Named {
std::string name_;
void setName(const std::string& name) {
name_ = name;
}
const std::string& getName() const noexcept { return name_; }
};
template <typename OverloadsType>
struct Actor : public OverloadsType {
Actor() : OverloadsType() {}
template <typename OtherOverloads>
Actor(const Actor<OtherOverloads>& other_actor) {
// ???????
this->setName(other_actor.getName());
}
};
int main() {
Actor<Overloads<Human, Named>> named_human;
named_human.setName("Bob");
std::cout << named_human.getName() << '\n';
Actor<Overloads<Animal, Named>> named_animal;
Actor<Overloads<Animal, Named>> animal_once_a_human (named_human);
std::cout << animal_once_a_human.getName() << '\n';
}
The code works correctly, printing two Bobs: Link
I want two things
Make the conversion operator compiles even when OverloadsType and OtherOverloads aren't derived from Named (this->setName(other_actor.getName()); should be ignored or not compiled at all)
Generalize "transferring" information from (common) base classes, not only name
How can I do this?
Here's a basic blueprint. This can be further refined so that the concept actually checks that getName() return a std::string.
#include <string>
#include <iostream>
template<typename T>
concept has_a_name = requires(T &t) {
{ t.getName() };
};
template<typename T, typename U>
void give_name(const T &t, U &u)
{
}
template<has_a_name T, typename U>
void give_name(const T &t, U &u)
{
u.setName(t.getName());
}
struct tom {
std::string getName() const
{
return "Tom";
}
};
struct jerry {};
struct cartoon {
void setName(const std::string &s)
{
std::cout << s << "\n";
}
template<typename T>
cartoon(const T &t)
{
give_name(t, *this);
}
};
int main()
{
tom Tom;
jerry Jerry;
cartoon mgm{Tom}, mgm2{Jerry};
return 0;
}
As far as generalizing this goes, any possible "generic" way of defining getters and setters will either be even longer than this, or use arcane, cryptic, difficult to read templates that end up expressing very simple operations.
A simple concept that defines each class that implements a particular getter, and a pair of template functions that select a stub or the real deal, via simple overload resolution, is easy to read, understand and follow.
I want two things
Make the conversion operator compiles even when OverloadsType and OtherOverloads aren't derived from Named (this->setName(other_actor.getName()); should be ignored or not compiled at all)
Generalize "transferring" information from (common) base classes, not only name
How can I do this?
So, if the source also has the base class, it should be copied, and otherwise we should use a default constructed instance. This is straightforward.
We can define a function to extract that initializer, a copy of a base if there is one, otherwise a default constructed instance:
template <std::semiregular T, typename U>
constexpr auto extract(const U& u) -> T {
if constexpr (std::derived_from<U, T>) {
return u;
}
else {
return T();
}
}
Then we can use that to initialize the "overloads":
template <typename... Bases>
struct Overloads : public Bases... {
Overloads() = default;
template <typename OtherOverloads>
Overloads(const OtherOverloads& other_overloads) : Bases{extract<Bases>(other_overloads)}... {}
};
Then we can use that logic to initialize the actor:
template <typename OtherOverloads>
Actor(const Actor<OtherOverloads>& other_actor) : OverloadsType(other_actor) {}
See https://godbolt.org/z/ofjPb8x75
Related
Let's say I have the following class:
template <typename T>
class SomeClass : Parent<T>
{
public:
// I have a function such as this one:
T DoSomething(const T &t)
{
return t.DoSomething(some_data);
}
// But `T` might be a pointer, so sometimes I will need something like the following
// instead (which obviously doesn't work as given):
T DoSomething(const T &t)
{
return new T(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
I got stuck in a giant mess of template errors trying to implement this in any semi-nice way possible using template specialization.
In the end I came up with this very ugly solution:
template <typename T>
class SomeClass : Parent<T>
{
public:
T DoSomething(const T &x)
{
return Specializer<T>::Do(this, x);
}
private:
template <typename V>
struct Specializer {
static V Do(SomeClass *me, const V &x)
{
return x.DoSomething(me->some_data);
}
};
template <typename V>
struct Specializer<V*> {
static V* Do(SomeClass *me, const V *&x)
{
return new V(x->DoSomething(me->some_data));
}
};
XYZ some_data;
};
Is there a better way to do this that doesn't involve stuffing this function into a dummy class/struct and passing around my this pointer?
PS: In reality, this has nothing to do with pointers, but rather with different types of containers. Pointers were just an easy example to use here.
You can avoid writing any specializations, and use a type trait like std::is_pointer along with if constexpr to decide what code to execute depending on the whether the type is a pointer type or not:
auto DoSomething(const T &t)
{
if constexpr (std::is_pointer_v<T>)
return new T(t->DoSomething(some_data));
else
return t.DoSomething(some_data);
}
If you don't want to check for whether T is a pointer, but want to check something else, you can still use this pattern by dropping in a suitable replacement for is_pointer.
If you have access to c++20, you can clean up the need for any SFINAE, specializations, or if constexpr by using concepts and constraints instead. This just allows you to define the same function N times with different criteria for its insantiation, which is much more readable IMO.
This is almost the same as the SFINAE approach, but without the need for the awful syntax (no std::declval, decltype, etc). It also doesn't require all implementations to exist in one function definition like the if constexpr approach; all you need is separate function definitions with different requires clauses:
#include <concepts>
...
template <typename T>
class SomeClass : Parent<T>
{
public:
// Work for everything that's not specialized
void DoSomething(const T &t)
{
std::cout << "Basic overload" << std::endl;
}
// Only work for pointers
void DoSomething(const T& t) requires(std::is_pointer_v<T>)
{
std::cout << "Pointer overload" << std::endl;
}
// Only work if T is convertible to SomeType
void DoSomething(const T& t) requires(std::convertible_to<T, SomeType>)
{
std::cout << "Convertible to SomeType overload" << std::endl;
}
private:
XYZ some_data;
};
Live Example
In this approach there are 3 different entries:
The basic fallback for all templates
An implementation that works for any pointer type, and
An implementation that works for any T type that may be convertible to SomeType
What about using SFINAE?
For example
#include <utility>
#include <iostream>
template <typename>
struct Parent
{ };
using XYZ = int;
template <typename T>
class SomeClass : Parent<T>
{
public:
template <typename U = T>
auto DoSomething (T const & t)
-> decltype( std::declval<U>().DoSomething(std::declval<XYZ>()) )
{ std::cout << "ref\n"; return t.DoSomething(some_data); }
template <typename U = T>
auto DoSomething (T const & t)
-> std::remove_reference_t<
decltype( std::declval<U>()->DoSomething(std::declval<XYZ>()),
std::declval<T>() )>
{
using V = std::remove_reference_t<decltype(*t)>;
std::cout << "pnt\n"; return new V(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
struct foo
{
foo (foo*) {}
foo () {}
foo DoSomething (int) const { return {}; }
} ;
int main()
{
SomeClass<foo> sc1;
SomeClass<foo*> sc2;
foo f;
sc1.DoSomething(f);
sc2.DoSomething(&f);
}
I mean: what about enabling the first version if, and only if, T is a type that supports a DoSomething(XYZ) method and enabling the second version if, and only if, T is a pointer of a type that supports a DoSomething(XYZ) method?
I am trying to provide an interface description for a free function listenTo(SomeAnimal) that should operate on types that fulfil particular type requirements (it should be an animal). The function arguments should not use the mechanism of interface inheritance with pure virtual methods.
I hacked a solution where the free function checks the argument type via an sfinae statement for a base class. To guarantee that the argument implements the interface of the base class I deleted the base class methods using = delete. I did not find any similar solution on the internet, thus, I am not sure if it makes sense, but it works.
Here it is, any opinions ?
#include <iostream>
#include <type_traits>
class IAnimal {
public:
// Interface that needs to be implemented
std::string sound() const = delete;
protected:
IAnimal(){}
};
class Cat : public IAnimal {
public:
// Implements deleted method
std::string sound() const {
return std::string("Meow");
}
};
class WildCat : public Cat {
public:
// Overwrites Cat sound method
std::string sound() const {
return std::string("Rarr");
}
};
class Dog : public IAnimal{
public:
// Implements deleted method
std::string sound() const {
return std::string("Wuff");
}
};
class Car {
public:
// Implements deleted method
std::string sound() const {
return std::string("Brum");
}
};
// Sfinae tests for proper inheritance
template<class TAnimal,
typename = std::enable_if_t<std::is_base_of<IAnimal, TAnimal>::value> >
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
int main(){
// Objects of type IAnimal can not be instanciated
// IAnimal a;
// Cats and Dogs behave like IAnimals
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
// listenTo(car);
return 0;
}
C++ doesn't have yet Concepts :-( but gcc-6 implements it:
template <class T>
concept bool Animal() {
return requires(const T& a) {
{a.sound()} -> std::string;
};
}
void listenTo(const Animal& animal) {
std::cout << animal.sound() << std::endl;
}
Demo
But you can create traits relatively easily with is-detected:
typename <typename T>
using sound_type = decltype(std::declval<const T&>().sound());
template <typename T>
using has_sound = is_detected<sound_type, T>;
template <typename T>
using is_animal = has_sound<T>;
// or std::conditional_t<has_sound<T>::value /*&& other_conditions*/,
// std::true_type, std::false_type>;
And then regular SFINAE:
template<class T>
std::enable_if_t<is_animal<T>::value>
listenTo(const T& animal) {
std::cout << animal.sound() << std::endl;
}
Another way, avoiding the complication of inheritance, is to create a type trait:
#include <iostream>
#include <type_traits>
template<class T>
struct is_animal : std::false_type {};
class Cat {
public:
std::string sound() const {
return std::string("Meow");
}
};
template<> struct is_animal<Cat> : std::true_type {};
class WildCat : public Cat {
public:
// Overwrites Cat sound method
std::string sound() const {
return std::string("Rarr");
}
};
template<> struct is_animal<WildCat> : std::true_type {};
class Dog {
public:
std::string sound() const {
return std::string("Wuff");
}
};
template<> struct is_animal<Dog> : std::true_type {};
class Car {
public:
std::string sound() const {
return std::string("Brum");
}
};
// Sfinae tests for proper inheritance
template<class TAnimal,
typename = std::enable_if_t<is_animal<TAnimal>::value> >
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
int main(){
// Objects of type IAnimal can not be instanciated
// IAnimal a;
// Cats and Dogs behave like IAnimals
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
// listenTo(car);
return 0;
}
namespace details {
template<template<class...>class Z, class always_void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
This is a meta type trait that helps write other type traits.
template<class T>
using sound_result = decltype( std::declval<T>().sound() );
sound_result<T> is the result of t.sound() where t is of type T.
template<class T>
using can_sound = can_apply<sound_result, T>;
can_sound<T> is a true type if and only if t.sound() is valid to call.
We can now say that animals are things that can sound.
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<class T>
using is_animal = bool_t< can_sound<T>{} >; // add more requirements
template<class TAnimal,
std::enable_if_t< is_animal<TAnimal const&>{}, int> =0
>
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
We get an error saying there is no matching overload if we try to listenTo(0) or somesuch.
Requiring that .sound() return something streamable can be written as well.
template<class T>
using stream_result = decltype( std::declval<std::ostream&>() << std::declval<T>() );
template<class T>
using can_stream = can_apply< stream_result, T >;
template<class T>
using stream_sound_result = stream_result< sound_result< T > >;
template<class T>
using can_stream_sound = can_apply< stream_sound_result, T >;
Now we can upgrade our animal test:
template<class T>
using is_animal = bool_t< can_stream_sound<T>{} >;
You didn't ask for an alternative solution. Instead, you asked for an opinion about your solution.
Well, here is my opinion, hoping it can help you.
That's a weak sfinae expression. You can easily break it using:
listenTo<Car, void>(car);
At least, I'd suggest you to rewrite your function as it follows:
template<class TAnimal>
std::enable_if_t<std::is_base_of<IAnimal, TAnimal>::value>
listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
That said, as it stands, you don't really need to use neither std::enable_if_t nor any other sfinae expression.
In this case, a static_assert is more than enough:
template<class TAnimal>
void listenTo(TAnimal const & a ) {
static_assert(std::is_base_of<IAnimal, TAnimal>::value, "!");
std::cout << a.sound() << std::endl;
}
This way you can also remove the useless definition of sound from IAnimal and still you'll have a nice compilation error.
Now, if you want to drop also the IAnimal interface, a possible solution (that hasn't been mentioned by other answer) follows:
#include <iostream>
#include <type_traits>
template<typename> struct tag {};
template<typename... T> struct check;
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
template<>
struct check<> {
template<typename T>
static constexpr bool verify(tag<T>) { return false; }
};
class Cat {
public:
std::string sound() const { return std::string("Meow"); }
};
class WildCat {
public:
std::string sound() const { return std::string("Rarr"); }
};
class Dog {
public:
std::string sound() const { return std::string("Wuff"); }
};
class Car {
public:
std::string sound() const { return std::string("Brum"); }
};
using AnimalCheck = check<Cat, WildCat, Dog>;
template<class TAnimal>
void listenTo(TAnimal const & a ) {
static_assert(AnimalCheck::verify(tag<TAnimal>{}), "!");
std::cout << a.sound() << std::endl;
}
int main(){
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
//listenTo(car);
return 0;
}
As requested in the comments, you can centralize the check of the existence of the method to be called in the check class.
As an example:
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
static constexpr auto test()
-> decltype(std::declval<T>().sound(), bool{})
{ return true; }
static_assert(test(), "!");
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
Or a more compact version:
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
static_assert(decltype(std::declval<T>().sound(), std::true_type{}){}, "!");
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
This is somehow a way of checking for a concept by using only features from the current revision of the language.
Note that concepts would have helped to do the same somehow and somewhere in the code, but they are not part of the standard yet.
deleteing a function removes it, it doesn't introduce a dependency on it. It says "this class does not have this function". So as far as implementing/annotating an interface goes, it's a bizarre way to achieve the goal. It's a bit like building a full-cockpit F-32 simulator and telling a very confused first test pilot "well we removed all the buttons so you'll know what actually exists in a real plane".
The way interfaces are implemented in C++ is with virtual functions, and you annotate a virtual function as being "pure" (to be implemented) by giving them a body of "0", like this:
struct IFace {
virtual void sound() = 0;
};
This makes it impossible to create a concrete instance of IFace or any class that derives from it, until you reach a part of the hierarchy where sound() is implemented:
struct IAudible {
virtual void sound() const = 0;
};
struct Explosion : public IAudible {
// note the 'override' keyword, optional but helpful
virtual void sound() const override { std::cout << "Boom\n"; }
};
struct Word : public IAudible {
};
void announce(const IAudible& audible) {
audible.sound();
}
int main() {
Explosion e;
announce(e);
}
Demo here: http://ideone.com/mGnw6o
But if we try to instantiate "Word", we get a compiler error: http://ideone.com/jriyay
prog.cpp: In function 'int main()':
prog.cpp:21:14: error: cannot declare variable 'w' to be of abstract type 'Word'
Word w;
prog.cpp:11:12: note: because the following virtual functions are pure within 'Word':
struct Word : public IAudible {
^
prog.cpp:4:22: note: virtual void IAudible::sound() const
virtual void sound() const = 0;
To implement a property system for polymorphic objects, I first declared the following structure:
enum class access_rights_t
{
NONE = 0,
READ = 1 << 0,
WRITE = 1 << 1,
READ_WRITE = READ | WRITE
};
struct property_format
{
type_index type;
string name;
access_rights_t access_rights;
};
So a property is defined with a type, a name and access rights (read-only, write-only or read-write). Then I started the property class as follows:
template<typename Base>
class property : property_format
{
public:
template<typename Derived, typename T>
using get_t = function<T(const Derived&)>;
template<typename Derived, typename T>
using set_t = function<void(Derived&, const T&)>;
private:
get_t<Base, any> get_f;
set_t<Base, any> set_f;
The property is associated to a base type, but may (and will) be filled with accessors associated to an instance of a derived type. The accessors will be encapsulated with functions accessing std::any objects on an instance of type Base. The get and set methods are declared as follows (type checking are not shown here to make the code minimal):
public:
template<typename T>
T get(const Base& b) const
{
return any_cast<T>(this->get_f(b));
}
template<typename T>
void set(Base& b, const T& value_)
{
this->set_f(b, any(value_));
}
Then the constructors (access rights are set to NONE to make the code minimal):
template<typename Derived, typename T>
property(
const string& name_,
get_t<Derived, T> get_,
set_t<Derived, T> set_ = nullptr
):
property_format{
typeid(T),
name_,
access_rights_t::NONE
},
get_f{caller<Derived, T>{get_}},
set_f{caller<Derived, T>{set_}}
{
}
template<typename Derived, typename T>
property(
const string& name_,
set_t<Derived, T> set_
):
property{
name_,
nullptr,
set_
}
{
}
The functions passed as arguments are encapsulated through the helper structure caller:
private:
template<typename Derived, typename T>
struct caller
{
get_t<Derived, T> get_f;
set_t<Derived, T> set_f;
caller(get_t<Derived, T> get_):
get_f{get_}
{
}
caller(set_t<Derived, T> set_):
set_f{set_}
{
}
any operator()(const Base& object_)
{
return any{
this->get_f(
static_cast<const Derived&>(object_)
)
};
}
void operator()(Base& object_, const any& value_)
{
this->set_f(
static_cast<Derived&>(object_),
any_cast<Value>(value_)
);
}
};
Now, considering these dummy classes.
struct foo
{
};
struct bar : foo
{
int i, j;
bar(int i_, int j_):
i{i_},
j{j_}
{
}
int get_i() const {return i;}
void set_i(const int& i_) { this->i = i_; }
};
I can write the following code:
int main()
{
// declare accessors through bar methods
property<foo>::get_t<bar, int> get_i = &bar::get_i;
property<foo>::set_t<bar, int> set_i = &bar::set_i;
// declare a read-write property
property<foo> p_i{"bar_i", get_i, set_i};
// declare a getter through a lambda
property<foo>::get_t<bar, int> get_j = [](const bar& b_){ return b_.j; };
// declare a read-only property
property<foo> p_j{"bar_j", get_j};
// dummy usage
bar b{42, 24};
foo& f = b;
cout << p_i.get<int>(f) << " " << p_j.get<int>(f) << endl;
p_i.set<int>(f, 43);
cout << p_i.get<int>(f) << endl;
}
My problem is that template type deduction doesn't allow me to declare a property directly passing the accessors as arguments, as in:
property<foo> p_i{"bar_i", &bar::get_i, &bar::set_i};
Which produces the following error:
prog.cc:62:5: note: template argument deduction/substitution failed:
prog.cc:149:50: note: mismatched types std::function<void(Type&, const Value&)> and int (bar::*)() const
property<foo> p_i{"bar_i", &bar::get_i, set_i};
Is there a way to address this problem while keeping the code "simple"?
A complete live example is available here.
std::function is a type erasure type. Type erasure types are not suitable for deduction.
template<typename Derived, typename T>
using get_t = function<T(const Derived&)>;
get_t is an alias to a type erasure type. Ditto.
Create traits classes:
template<class T>
struct gettor_traits : std::false_type {};
this will tell you if T is a valid gettor, and if so what its input and output types are. Similarly for settor_traits.
So
template<class T, class Derived>
struct gettor_traits< std::function<T(Derived const&)> >:
std::true_type
{
using return_type = T;
using argument_type = Derived;
};
template<class T, class Derived>
struct gettor_traits< T(Derived::*)() >:
std::true_type
{
using return_type = T;
using argument_type = Derived;
};
etc.
Now we got back to the property ctor:
template<class Gettor,
std::enable_if_t< gettor_traits<Gettor>{}, int> =0,
class T = typename gettor_traits<Gettor>::return_value,
class Derived = typename gettor_traits<Gettor>::argument_type
>
property(
const string& name_,
Gettor get_
):
property_format{
typeid(T),
name_,
access_rights_t::NONE
},
get_f{caller<Derived, T>{get_}},
nullptr
{
}
where we use SFINAE to ensure that our Gettor passes muster, and the traits class to extract the types we care about.
There is going to be lots of work here. But it is write-once work.
My preferred syntax in these cases would be:
std::cout << (f->*p_i)();
and
(f->*p_i)(7);
where the property acts like a member function pointer, or even
(f->*p_i) = 7;
std::cout << (f->*p_i);
where the property transparently acts like a member variable pointer.
In both cases, through overload of ->*, and in the second case via returning a pseudo-reference from ->*.
At the end of this answer is a slightly different approach. I will begin with the general problem though.
The problem is &bar::get_i is a function pointer to a member function while your alias is creating a function object which needs the class as additional template parameter.
Some examples:
Non member function:
#include <functional>
void a(int i) {};
void f(std::function<void(int)> func)
{
}
int main()
{
f(&a);
return 0;
}
This works fine. Now if I change a into a struct:
#include <functional>
struct A
{
void a(int i) {};
};
void f(std::function<void(int)> func)
{
}
int main()
{
f(std::function<void(int)>(&A::a));
return 0;
}
this gets the error:
error: no matching function for call to std::function<void(int)>::function(void (A::*)(int))'
because the std::function object also need the base class (as you do with your alias declaration)
You need a std::function<void(A,int)>
You cannot make your example much better though.
A way to make it a "bit" more easier than your example would maybe be this approach using CRTP.
#include <functional>
template <typename Class>
struct funcPtr
{
template <typename type>
using fun = std::function<void(Class,type)>;
};
struct A : public funcPtr<A>
{
void a(int i) {};
};
void f(A::fun<int> func)
{
};
int main()
{
f(A::fun<int>(&A::a));
return 0;
}
And each your "derived" classes derives from a funcPtr class which "auto generates" the specific alias declaration.
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 want to make a function called debug that outputs some info about objects. My system contains objects of many different types; some of them contain other objects.
using namespace std; // for brevity
struct dog {string name;};
struct human {string name; string address;};
struct line {list<human*> contents;};
struct pack {vector<dog*> contents;};
I want the function to output the member name of the argument if it has one, or debug the contents member of the argument if it has one.
I came up with the following code:
template <class T>
void debug(T object) // T here is a simple object like dog, human, etc
{
cout << object.name.c_str() << '\n';
}
// A helper function, not really important
template <class T>
void debug_pointer(T* object)
{
debug(*object);
}
void debug(pack object)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<dog>);
}
void debug(line object)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<human>);
}
Here, the code for pack and line is nearly identical! I would like to avoid writing the same code several times:
struct line {list<human*> contents; typedef human type;};
struct pack {vector<dog*> contents; typedef dog type;};
template <class T>
void debug(T object) // T here is a compound object (having contents)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<T::type>);
}
But this syntax conflicts with the function template for the "simple" objects (has the same signature).
How can i rewrite my code? I don't want to rewrite the first part (declarations for dog, human, etc) because that part of my program is already very complicated, and adding stuff (base classes, member functions, etc) to it just for debugging seems out of place.
The basic code could look like this:
template <typename T> void debug(T const & x)
{
debug_helper<T, has_name<T>::value>::print(x);
}
We need a helper class:
template <typename, bool> struct debug_helper;
template <typename T> struct debug_helper<T, true>
{
static void print(T const & x) { /* print x.name */ }
};
template <typename T> struct debug_helper<T, false>
{
static void print(T const & x) { /* print x.content */ }
};
Now we just need a SFINAE trait class has_name<T>, and a mechanism to print containers. Both those problems are solved almost verbatim in the pretty printer code.
Make the container a template parameter as well:
template <template <typename> class Container, typename T>
void debug(Container<T> object)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}
BTW, most of the cases you may want to pass by const reference instead of by value (which requires copying the whole vector/list):
template <template <typename> class Container, typename T>
void debug(const Container<T>& object)
If C++11 can be used, you could use decltype to determine the T from the contents:
template <typename T>
void debug(const T& object)
{
typedef decltype(*object.contents.front()) T;
for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}
GCC also has typeof when C++11 cannot be used.
Using C++11, decltype and SFINAE make things easy :)
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
struct dog { std::string name; };
struct human { std::string name; std::string address; };
struct line { std::list<human*> contents; };
struct pack { std::vector<dog*> contents; };
template <typename T>
auto debug(T const& t) -> decltype(t.name, void(0)) {
std::cout << t.name << '\n';
}
template <typename T>
auto debug(T const* t) -> decltype(t->name, void(0)) {
if (t != 0) std::cout << t->name << '\n';
}
struct Debugger {
template <typename T>
void operator()(T const& t) { debug(t); }
};
template <typename C>
auto debug(C const& c) -> decltype(c.contents, void(0)) {
typedef decltype(c.contents) contents_type;
typedef typename contents_type::value_type type;
std::for_each(c.contents.begin(), c.contents.end(), Debugger());
}
int main() {
dog dog1 = { "dog1" }, dog2 = { "dog2" };
human h1 = { "h1" }, h2 = { "h2" };
line l; l.contents.push_back(&h1); l.contents.push_back(&h2);
debug(l);
}
In action at ideone this yields:
h1
h2
as expected :)
Without C++11, it requires some little craft but the principle remains the same, using boost::enable_if you need to create a structure that will provoke a compilation error based on the presence and accessibility of name and contents.
Of course, it would all easier if you simply hooked up the methods in the structures themselves :)
You can use SFINAE to select the overload being used.
I forget the exact details, but you can use it to detect the presence of a "contents" member or a "name" member and then overload based on that.