I have some hard time implementing traits in C++, I tried to follow several examples from the internet but it still doesn't want to compile.
I use a Term class, which contains an Attribute, an Operator and sometimes a value. For example, age < 10 or color == red are (simple) terms. Different kinds of attributes or operators exists, that inherits from classes Attribute or TermOperator.
Since a lot of methods of the term class will depend on the attribute and the operator, this is a template class. In order to simplify the manipulation of terms, I added an abstract class : AbstractTerm
class AbstractTerm {
protected:
Attribute* pAttribute;
TermOperator* pOperator;
public:
virtual bool eval(Data *) const = 0;
};
template <typename ATT, typename OP>
class Term : AbstractTerm {
typedef typename TermTraits<ATT,OP>::type VALUE_TYPE;
private:
typename TermTraits<ATT,OP>::type value;
public:
bool eval(Data *) const;
};
The value I need to store in term will depend on both attribute & operator, so I use traits to obtain the right type to store the value.
template < typename ATT, typename OP>
struct TermTraits
{
typedef int type;
};
template <>
struct TermTraits<ListAttribute, TermOperator>
{
typedef ListValue type;
};
template <>
struct TermTraits<NumericAttribute, TermOperator>
{
typedef NumericIntValue type;
};
However, in the eval method when I use VALUE_TYPE I don't get the right type
template <> bool Term<NumericAttribute, TermOperatorEquals>::eval(Data _data) const {
// VALUE_TYPE is a int, but it should be a NumericIntValue
VALUE_TYPE *pValue = data->getValue<VALUE_TYPE>(this->pAttribute->getId());
return !pValue->isEmpty && (this->value == pValue->value); // I get compilation errors here because pValue is a int* and not a 'NumericIntValue*'
};
I get the error:
error: request for member 'isEmpty' in '* pValue',
which is of non-class type 'Term<NumericAttribute,
TermOperatorExists>::VALUE_TYPE {aka int}.
I can't figure out why it doesn't use the specialisation TermTraits<NumericAttribute, TermOperator>, since TermOperatorExists inherits from TermOperator.
Traits are a new concept to me, so maybe I made some obvious mistakes. If someone has a better way or simpler way to do this I'm also interested.
Although TermOperatorExists inherits from TermOperator, those are different types, so template specialization is not called for TermOperatorExists. You need to explicitly cast TermOperatorExists to its base class in order to get specialization called.
Example:
#include <iostream>
using namespace std;
class base
{
};
class derrived: public base
{
};
class test
{
public:
template <class T> void print(T arg)
{
std::cout << "test" << std::endl;
}
};
template <>
void test::print<base>(base arg)
{
std::cout << "base specialiation" << std::endl;
};
int main()
{
cout << "Hello World" << endl;
base b;
derrived d;
test t;
t.print<int>(1);
t.print(b);
t.print(d);
t.print(static_cast<base>(d));
return 0;
}
outputs:
Hello World
test
base specialiation
test
base specialiation
Related
I have some code that basically do this:
struct Base {
virtual ~Base() = default;
virtual int forward() = 0;
};
struct Derived : Base {
int forward() override {
return 42;
}
};
typename std::aligned_storage<sizeof(Derived), alignof(Derived)>::type storage;
new (&storage) Derived{};
auto&& base = *reinterpret_cast<Base*>(&storage);
std::cout << base.forward() << std::endl;
I highly doubt it's well defined behaviour. If it's indeed undefined behaviour, how can I fix it? In the code that do the reinterpret_cast, I only know the type of the base class.
On the other hand, if it's well defined behavior in all cases, why is this working and how?
Just keeping a reference to the contained object is not applicable here. In my code I want to apply SBO on a type erased list where the type is created by the user of my library, and basically extends the Base class.
I add elements inside a template function, but in the function that reads it, I cannot know the Derived type. The whole reason why I use a base class is because I only need the forward function in my code that reads it.
Here's what my code looks like:
union Storage {
// not used in this example, but it is in my code
void* pointer;
template<typename T>
Storage(T t) noexcept : storage{} {
new (&storage) T{std::move(t)}
}
// This will be the only active member for this example
std::aligned_storage<16, 8> storage = {};
};
template<typename Data>
struct Base {
virtual Data forward();
};
template<typename Data, typename T>
struct Derived : Base<Data> {
Derived(T inst) noexcept : instance{std::move(inst)} {}
Data forward() override {
return instance.forward();
}
T instance;
};
template<typename> type_id(){}
using type_id_t = void(*)();
std::unordered_map<type_id_t, Storage> superList;
template<typename T>
void addToList(T type) {
using Data = decltype(type.forward());
superList.emplace(type_id<Data>, Derived<Data, T>{std::move(type)});
}
template<typename Data>
auto getForwardResult() -> Data {
auto it = superList.find(type_id<Data>);
if (it != superList.end()) {
// I expect the cast to be valid... how to do it?
return reinterpret_cast<Base<Data>*>(it->second.storage)->forward();
}
return {};
}
// These two function are in very distant parts of code.
void insert() {
struct A { int forward() { return 1; } };
struct B { float forward() { return 1.f; } };
struct C { const char* forward() { return "hello"; } };
addToList(A{});
addToList(B{});
addToList(C{});
}
void print() {
std::cout << getForwardResult<int>() << std::endl;
std::cout << getForwardResult<float>() << std::endl;
std::cout << getForwardResult<const char*>() << std::endl;
}
int main() {
insert();
print();
}
Not sure about the exact semantics of whether reinterpret_cast is required to work with base class types, but you can always do this,
typename std::aligned_storage<sizeof(Derived), alignof(Derived)>::type storage;
auto derived_ptr = new (&storage) Derived{};
auto base_ptr = static_cast<Base*>(derived_ptr);
std::cout << base_ptr->forward() << std::endl;
Also why use the auto&& with the base reference in your code?
If you only know the type of the base class in your code then consider using a simple trait in an abstraction for the aligned_storage
template <typename Type>
struct TypeAwareAlignedStorage {
using value_type = Type;
using type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
};
and then you can now use the storage object to get the type it represents
template <typename StorageType>
void cast_to_base(StorageType& storage) {
using DerivedType = std::decay_t<StorageType>::value_type;
auto& derived_ref = *(reinterpret_cast<DerivedType*>(&storage));
Base& base_ref = derived_ref;
base_ref.forward();
}
If you want this to work with perfect forwarding, then use a simple forwarding trait
namespace detail {
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl;
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<TypeToMatch&, Type> {
using type = Type&;
};
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<const TypeToMatch&, Type> {
using type = const Type&;
};
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<TypeToMatch&&, Type> {
using type = Type&&;
};
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<const TypeToMatch&&, Type> {
using type = const Type&&;
};
}
template <typename TypeToMatch, typename Type>
struct MatchReference {
using type = typename detail::MatchReferenceImpl<TypeToMatch, Type>::type;
};
template <typename StorageType>
void cast_to_base(StorageType&& storage) {
using DerivedType = std::decay_t<StorageType>::value_type;
auto& derived_ref = *(reinterpret_cast<DerivedType*>(&storage));
typename MatchReference<StorageType&&, Base>::type base_ref = derived_ref;
std::forward<decltype(base_ref)>(base_ref).forward();
}
If you are using type erasure to create your derived class types which you then add to a homogenous container, you could do something like this
struct Base {
public:
virtual ~Base() = default;
virtual int forward() = 0;
};
/**
* An abstract base mixin that forces definition of a type erasure utility
*/
template <typename Base>
struct GetBasePtr {
public:
Base* get_base_ptr() = 0;
};
template <DerivedType>
class DerivedWrapper : public GetBasePtr<Base> {
public:
// assert that the derived type is actually a derived type
static_assert(std::is_base_of<Base, std::decay_t<DerivedType>>::value, "");
// forward the instance to the internal storage
template <typename T>
DerivedWrapper(T&& storage_in) {
new (&this->storage) DerivedType{std::forward<T>(storage_in)};
}
Base* get_base_ptr() override {
return reinterpret_cast<DerivedType*>(&this->storage);
}
private:
std::aligned_storage_t<sizeof(DerivedType), alignof(DerivedType)> storage;
};
// the homogenous container, global for explanation purposes
std::unordered_map<IdType, std::unique_ptr<GetBasePtr<Base>>> homogenous_container;
template <typename DerivedType>
void add_to_homogenous_collection(IdType id, DerivedType&& object) {
using ToBeErased = DerivedWrapper<std::decay_t<DerivedType>>;
auto ptr = std::unique_ptr<GetBasePtr<Base>>{
std::make_unique<ToBeErased>(std::forward<DerivedType>(object))};
homogenous_container.insert(std::make_pair(id, std::move(ptr)));
}
// and then
homogenous_container[id]->get_base_ptr()->forward();
You may simply do
auto* derived = new (&storage) Derived{};
Base* base = derived;
So no reinterpret_cast.
In the 'simple' exmaple you have, since you are casting from derived to base, either static_cast or dynamic_cast will work.
The more complex use case will end in tears, because the underlying values of a base pointer and a derived pointer to the same object need not be equal. It might work today, but fail tomorrow:
reinterpret_cast does not play well with inheritance, especially multiple inheritance. If you ever to inherit from multiple basesand the first base class has size (or not have size if empty base optimization is not performed), reinterpret_cast to the second base class from an unrelated type will not apply the offset.
Overloading does not play well with overriding. Templated classes should not have virtual methods. Templated classes with virtual methods should not be used with too much type deduction.
The undefined behavior is fundamental to the manner in which MI is specified in C++, and unavoidable because you are trying to obtain something (in compile time) that you deliberated erased (in compile time). Just drop every virtual keyword from that class and implement everything with templates and everything will be more simple and correct.
Are you sure your derived class objects can fit within 16 bytes? You probably need some static_assert.
If you are willing to stomach the performance penalty introduced by virtual functions, why care about alignment?
What is the workaround to get this to compile?
#include <iostream>
template <typename Derived>
struct CRTP {
void foo (const typename Derived::type& a) {std::cout << a << '\n';}
};
struct A : CRTP<A> {
using type = int;
};
struct B : CRTP<B> {
using type = std::string;
};
// etc...
int main() {
A a;
a.foo(5);
}
This will not compile, because at the time of instantiation of CRTP<A>, A isn't a complete class yet, so A::type cannot be accessed. But what is the workaround? I need this type of design so that the foo function can be used generically for many different classes.
A somewhat crazier alternative is to defer evaluation until an attempt is made to call foo, by which point Derived would be complete. This requires making it a template.
template <typename Derived>
struct CRTP {
template<class T = Derived>
void foo (const typename T::type& a) {std::cout << a << '\n';}
};
It is trivial to block calling foo with a type that isn't Derived, if desired, via a static_assert.
I'm pretty sure you can't use the CRTP on a 'using' case. You can use it for methods and members, but not things like types. When using templates though, having types as template parameters is what it is so useful for, so why not do
template <typename Derived, typename Type>
....
Which will work perfectly fine.
I am trying to do the following: a templated class should provide some functions dependend on whether or not the type it has been templated with contains a member variable with a given name. As example the following pseudocode which should provide "printid()" only when templated struct/class has a member called "id":
#include <iostream>
#include <type_traits>
struct A { int id; };
struct B { };
template<typename T>
class foo
{
T myvar;
public:
#if exists T.id (or the alternative: #if exists myvar.id)
printid() { std::cout << "I have element id."; }
#endif
};
int main(){
foo<A> ok;
ok.printid(); // should compile and execute
foo<B> nok;
nok.printid(); // should not compile
return 0;
}
Digging around SFINAE, traits, std::enable_if and StackOverflow, I think it can be done ... somehow. But I somehow fail to combine enable_if with the the following snippet from the question How to detect whether there is a specific member variable in class?:
template<typename T, typename = void>
struct has_id : std::false_type { };
template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };
Any help appreciated.
Yep, it's possible. Here's an example:
template<typename T>
class foo
{
T myvar;
public:
template <class _T = T,
class = typename std::enable_if<
!std::is_function<decltype(_T::id)>::value>
::type>
void printid() { std::cout << "I have element id."; }
};
Specifically, note how we're "taking in" T as _T in order to not force a constraint on the class template parameter (which would make the class itself un-compileable). Instead, we're creating a new, independent template member function, which doesn't force anything on T itself—it just "happens to" use it as a default argument. That's the key part.
Assume the following situation:
There is a templated interface defining a set of operations on different data types.
This interface is implemented by various specialized classes defining the operations for actual data types.
There is some managing class that has to work instances of classes as defined in 2.
Simplified example code could look like this:
#include <iostream>
#include <type_traits>
template <typename R, typename S>
class ICanDoIt
{
public:
virtual void doStuff() = 0;
protected:
ICanDoIt<R, S>(R rA, S sA) : r(rA), s(sA) {};
R r;
S s;
};
class DoesIt : public ICanDoIt<int, double>
{
public:
DoesIt(int iA, double dA) : ICanDoIt(iA, dA) {};
virtual void doStuff()
{ std::cout << "r * s = " << r * s << " done." << std::endl; }
};
template <typename T>
class NeedsSomeoneWhoCanDoIt
{
static_assert(std::is_base_of<ICanDoIt<R, S>, T>::value,
"T needs to be able to do it.");
public:
NeedsSomeoneWhoCanDoIt(const T& doesItA) : doesIt(doesItA) {};
void getItDone() { doesIt.doStuff(); };
private:
T doesIt;
};
int main()
{
DoesIt doesIt(5, 2.2);
NeedsSomeoneWhoCanDoIt<DoesIt> needsIt(doesIt);
needsIt.getItDone();
}
If you untemplate the interface "ICanDoIt" the code will actually work. But the static_assert for the templated version will fail because ICanDoIt's template arguments are wrapped and hidden by the specialization performed in the decalaration of DoesIt.
How can I limit the managing classes (NeedsSomeoneWhoCanDoIt) template parameter "T" to any specialization of ICanDoIt, regardless of which type has been chosen for R, S during the specialization of ICanDoIt?
You could always make the actual types for R and S used to instantiate ICanDoIt accessible to a derived class, i.e.
template <typename R, typename S> class ICanDoIt {
public:
typedef R R_t;
typedef S S_t;
virtual void doStuff() = 0;
};
so that your static_assert would become
static_assert(std::is_base_of<ICanDoIt<typename T::R_t, typename T::S_t>,
T>::value,
"T needs to be able to do it.");
Depending on what your actual code looks like the design might become clearer if you'd define a purely abstract base class (i.e. an actual type ICanDoItBase instead of a template) from which you'd inherit the currently templated functionality in ICanDoIt which would again be a base of DoesIt.
NeedsSomeoneWhoCanDoIt could then directly use the the polymorphic base class ICanDoItBase without any needs for additional type checks.
You don't need to publish the template parameters. The standard SFINAE-based approach would work just fine.
namespace detail {
template<class R, class S>
std::true_type test(ICanDoIt<R, S>*);
std::false_type test(...);
}
template<class T>
using can_do_it = decltype(detail::test((T*)nullptr));
If I have
template<class T>
TalkyBuffer& operator<<(T const &object) { // Template
...
}
TalkyBuffer& operator<<(TalkySerialisable const &object); // Override
and a class
class A : public TalkySerialisable {
...}
Then if I perform
TalkyBuffer b;
A test;
b << test;
Then gcc is calling the Template function rather than the Override function
However if I specifically define an override
TalkyBuffer& operator<<(A const &object); // Override without polymorphism
Then gcc picks that one.
Is there a practical way to override a templated function with an abstract class?
I read this but it doesn't shed light onto what happens when you throw polymorphism into the mix:
http://www.gotw.ca/publications/mill17.htm
Also I couldn't find a solution here but perhaps I'm using the wrong terms.
When defining the function TalkyBuffer& operator<<(TalkySerialisable const &object); You are not overriding. You are overloading the tmeplated function.
But, when the complier sees b << test;, it searches for an operator that wants an A. It has one, it's the templated function that requires no automatic cast. This is the best choice.
The overloaded function requires an automatic cast (from A to TalkySerialisable) on the parameters to fit the declaration, and is not the best choice.
I think it's possible to use a simple function based solution, reusing function overload for derivation.
struct specialized {};
struct generic {};
template <class T>
TalkyBuffer& serialize(TalkyBuffer& buffer, T const& object, generic) {
...
}
generic dispatch(...) {} // always picked up last in overload resolution
template <class T>
TalkyBuffer& TalkyBuffer::operator<<(T const& object) { // Template
return serialize(*this, object, dispatch(object));
}
Now, let's implement your custom class:
TalkyBuffer& serialize(TalkyBuffer& buffer,
TalkySerialisable const& object,
specialized);
specialized dispatch(TalkySerialisable const&) {}
And create a derived one:
class A: public TalkySerialisable {};
So, what happens ?
TalkyBuffer::operator<<(T const&) will be picked up
when trying to resolve the overload for serialize, it will first compute the result of dispatch
when resolving the result of dispatch, dispatch(TalkySerializable const&) is a better match than dispath(...), thus the return type is specialized
the generic serialize cannot be used (there is no conversion from specialized to generic), so inheritance kicks in
A solution using Boost.enable_if:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>
template<typename T>
typename boost::disable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object) { // Template for non TalkySerializable
...
}
template <typename T>
typename boost::enable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object); // Template overload for TalkySerializable
...
TalkyBuffer b;
A test;
b << test; // calls operator<< <A>(A const &), which instantiates
// the overload for TalkySerializable
b << 41; // calls operator<< <int>(int const &), which corresponds to
// the "default" overload
I'm not sure this is the best solution, but I failed to find a better one: specializing the template does not work either.
As #Matthieu noted in the comment, the previous solution has the major drawback that the base template needs to know that it will be overloaded, which is an unnecessary coupling that hinders extensibility.
To solve this problem, I came up with a new approach using tag dispatching, along with trait classes and compile-time introspection using Boost.MPL macros.
// TalkyBuffer.hpp
#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/has_xxx.hpp>
// defines a metafunction has_talky_buffer_tag<T> that allows us to know at
// compile-time if T has a member type named talky_buffer_tag
BOOST_MPL_HAS_XXX_TRAIT_DEF(talky_buffer_tag)
// tag for the default case
struct default_talky_buffer_tag {};
// trait class for obtaining the tag of a type
template <typename T, typename Enable = void >
struct talky_buffer_trait
{
typedef default_talky_buffer_tag type;
};
// specialization for types that provide a nested typedef
template <typename T>
struct talky_buffer_trait<T,
typename boost::enable_if<has_talky_buffer_tag<T> >::type>
{
typedef typename T::talky_buffer_tag type;
};
struct TalkyBuffer
{
// Insertion operator, which calls an implementation function that can
// be overloaded depending on the tag
template<typename T>
TalkyBuffer & operator<<(T const & object)
{
typename talky_buffer_trait<T>::type tag;
return insertionOperatorImpl(*this, object, tag);
}
};
// default implementation
template <typename T>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, T const & object,
default_talky_buffer_tag)
{
std::cout << "default";
return buf;
}
//-------
// TalkySerializable.hpp
struct TalkySerializable
{
struct tag {};
typedef tag talky_buffer_tag;
};
// A inherits from the nested typedef
struct A : public TalkySerializable {};
// implementation for TalkySerializable objects
template <typename Serializable>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, Serializable const & object,
TalkySerializable::tag)
{
std::cout << "specialized";
return buf;
}
//-------
int main()
{
TalkyBuffer b;
A test;
b << test; // outputs "specialized"
b << 41; // outputs "default"
}
To provide new implementations of the insertion operator for a given type T, one needs to provide a new type to act as a tag (TypeSerializable::tag in our example), provides a way to associate T with the new tag (either by using a nested typedef as in the example, or by specializing the trait class: template <> talky_buffer_trait<T> { typedef new_tag type };), and finally overload the implementation function (insertionOperatorImpl in the example).