C++ class template specialization with pointers - c++

I have a tree structure of the following format:
template <typename DataType>
class Tree {
DataType *accessData() { return data; }
Tree *child1, *child2;
DataType *data;
};
template <typename DataType>
class Root : public Tree<DataType> {
// root provides storage of nodes; when it goes out of scope, the
// entire tree becomes invalid
MemoryPool<Tree> nodeStorage;
MemoryPool<DataType> dataStorage;
};
I use a variety of instantiations of this template in my program. It works quite well.
One instantiation, however, uses a DataType which is just an enum (so it's the same size as a pointer!) and because speed is essential (both when the tree is built, and when it is accessed), I'd much rather have this instantiation use the enum directly instead of a pointer. An example how I'd like the code to look (not strict):
Tree<BigClass> *foo = ...;
foo->accessData()->doBigClassThings();
Tree<int> *bar = ...;
int x = 4 + bar->accessInt();
Now of course I could just keep the current templates but I don't like this extra pointer access and especially the need to allocate ints in the root. Any ideas on how I can specialize the template to provide this functionality, or other approaches?
I've tried to specialize the template like this (and a bazillion other ways)
template <> Tree<int> { ... }
But I just keep getting compile errors. Any help would be greatly appreciated!

I would recommend using a traits class to deduce the type of object stored in Tree.
// The default traits.
template <typename DataType> struct TreeDataType
{
using Type = DataType*;
};
template <typename DataType>
class Tree {
// Define the data type using the traits class.
using Data = typename TreeDataType<DataType>::Type;
Data accessData() { return data; }
Tree *child1, *child2;
Data data;
};
and then specialize TreeDataType for MyEnum.
template <> struct TreeDataType<MyEnum>
{
using Type = MyEnum;
};

I suggest defining multiple data classes with the same interface that you can use as DataType template parameters. Abstract the way the data is stored from the way the data is accessed.
template<typename T>
class value_data
{
private:
T _value;
public:
T& access() { return _value; }
const T& access() const { return _value; }
};
template<typename T>
class unique_ptr_data
{
private:
std::unique_ptr<T> _value;
public:
T& access() { assert(_value != nullptr); return *_value; }
const T& access() const { assert(_value != nullptr); return *_value; }
};
enum class my_enum { /* ... */ };
class my_enum_data
{
private:
my_enum _value;
public:
my_enum& access() { return _value; }
const my_enum& access() const { return _value; }
};
Then, in your Tree class, you can use them through their common interface:
template <typename DataType>
class Tree {
auto& accessData() { return data.access(); }
Tree *child1, *child2;
DataType data;
};

Related

Idiomatic C++11 for delegating template specialisations to a default implementation

I'm making a struct Box<T> that handles some data. The specifics are unimportant.
An important note however is that Box<T> can store a pointer, but it might not. So both Box<int> and Box<int *> are valid. Obviously, if we own Box.data, we're going to need to delete data if it is a pointer type.
Here's a solution I came up with that works in C++11:
template <typename T> struct BoxTraits;
template <typename T> struct Box {
using traits_t = BoxTraits<T>;
T data;
~Box() = default; // not required, I know
T get_data() { return traits_t::get_data(this); }
};
template <typename T> struct Box<T *> {
using traits_t = BoxTraits<T *>;
T *data;
~Box() { delete data; }
T *get_data() { return traits_t::get_data(this); }
};
template <typename T> struct BoxTraits {
static T get_data(Box<T> *const box) { return box->data; }
};
Box::get_data is here to illustrate an issue with this design pattern. For every single method I want to add to Box, I need to add some boiler plate in each specialisation. Note that I would also need a Box<T *const> specialisation.
This seems like quite a rubbish solution. In C++14, I could use if constexpr with a is_ptr<T> trait and only have to write extra code in the methods that need specialising... Is there any way I can do this is in C++11?
This solution is shorter, cleaner and works for Box<U *const>!
template <typename T> struct is_ptr { static const bool value = false; };
template <typename U> struct is_ptr<U *> { static const bool value = true; };
template <typename U> struct is_ptr<U *const> {
static const bool value = true;
};
template <typename T> struct Box {
T data;
~Box() {
if constexpr (is_ptr<T>::value) {
delete data;
}
}
T get_data() { return data; }
};
First off, C++11 already has std::is_pointer, no need to roll your own. You can see that it inherits from std::true_type or std::false_type instead of defining its own value member. The reason for that is tag dispatching, that can effectively replace if constexpr in this situation:
template <typename T> struct Box {
T data;
~Box() {
destroy(std::is_pointer<T>{});
}
private:
void destroy(std::true_type) {
delete data;
}
void destroy(std::false_type) {} // nothing to do
};
Demo
I think this is the most idiomatic way in C++11 for delegating to different implementations based on type traits. In many situations, tag dispatching can replace if constexpr (from C++17, not C++14), and I believe the latter always replaces the former in addition to being clearer. Tag dispatching can also be used before C++11 if you roll your own type traits.
Last note: you don't need to use the standard type traits, you can do something like this:
template <typename T> struct is_ptr { static const bool value = false; };
template <typename T> struct is_ptr<T*> { static const bool value = true; };
template <typename T> struct is_ptr<T* const> { static const bool value = true; };
template <typename T> struct is_ptr<T* volatile> { static const bool value = true; };
template <typename T> struct is_ptr<T* const volatile> { static const bool value = true; };
template<bool b>
struct bool_constant {};
template<typename T>
struct Box {
T data;
~Box() {
destroy(bool_constant<is_ptr<T>::value>{});
}
private:
void destroy(bool_constant<true>) {
delete data;
}
void destroy(bool_constant<false>) {} // nothing to do
};
Demo
However, this pretty much amounts to recreating the standard type traits, but probably worse. Just use the standard library when possible.
I think you had the right idea with the helper type, but I'd do it like the following example illustrates.
template <typename B, typename T>
struct BoxTraits {
static T& get_data(B *const box) { return box->data; }
// ^--- important
static T const& get_data(B const* const box) { return box->data; }
};
template <typename T>
struct BoxTraits<Box<T*>, T> {
static T& get_data(Box<T*>* const box) { return *box->data; }
static T const& get_data(Box<T*> const* const box) { return *box->data; }
};
Both versions always return T, so you can use them the same regardless of your Box's payload. You could add a type alias in Box so you don't have to pass the template arguments:
typedef Traits BoxTraits<Box, T>; // in Box class

Converting from abstract data type to generic type

I am trying to figure out how to convert from an abstract templated class to a generic data type as seen below. Specifically, A::m_data will hold class B<T> as a data type. Eventually I want to be able to convert A::m_data to whatever data type B::m_data is holding. In this case, to an integer.
Currently, I get the error on line 28:
binary '=': no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion).
template <typename T>
class B;
template <typename T>
class A
{
public:
T m_data;
operator A<B<T>>()
{
return T(m_data);
}
};
template <typename T>
class B
{
public:
T m_data;
};
int main()
{
A <B<int>> foo;
B<int> bar;
bar.m_data = 10;
foo.m_data = bar.m_data;
return 0;
}
The error is because foo.m_data is a B<int> and bar.m_data is an int, and you have not defined an operator= for B that takes a T as input, eg:
template <typename T>
class B
{
public:
T m_data;
B& operator=(const T &value) {
m_data = value;
return *this;
}
};
Live Demo
It is not really clear what your A::operator() is trying to return. But since you say you want A::m_data to eventually be the same type as B::m_data, you can use decltype() for that, eg:
template <typename T>
class A
{
public:
using data_type = decltype(T::m_data);
data_type m_data;
operator data_type()
{
return m_data;
}
};
Live Demo

How to specialize a non-templated-member function of a template class for multiple types?

I'm biting of my nails on the syntax required to partially specialize a member function for multiple types. Here is what I have:
#include <cstdint>
#include <string>
class Property
{
public:
virtual int read(uint8_t *) = 0;
};
template<typename T>
class PropertyValue
{
T value_;
public:
int read(uint8_t *);
};
// specialized for std::string
template<>
int PropertyValue<std::string>::read(uint8_t *buf) { /* put string-value to buf */}
Now I would want to specialize the read-function for different enum-types. I tried a combination of enable_if and is_same which looks promissing, then putting it inside the template-declaration (compiler told me there are now 2 template arguments whereas 1 was expected).
Putting it inside the class-definition was not working either. Outside ... well, here's what I currently have.
// specialize for some enums
template<typename T>
typename std::enable_if<std::is_same<T, enum Enum1>::value ||
std::is_same<T, enum Enum2>::value, int>::type
PropertyValue<T>::read(uint8_t *buf)
{
return encode_enum(buf, value_);
}
Where is my thinking wrong?
EDIT: Writing it like this compiles and works:
template<>
int PropertyValue<Enum 1>::read(uint8_t *buf)
{
return encode_enum(buf, value_);
}
template<>
int PropertyValue<Enum 2>::read(uint8_t *buf)
{
return encode_enum(buf, value_);
}
PropertyValue::value itself is not a template. It's not a template class, it's not a template function. It's a member of a template class, which is not the same thing as being a template itself.
You have to specialize the entire class.
template<>
class PropertyValue<std::string>
{
std::string value_;
public:
int read(uint8_t *)
{
// Your specialization goes here.
}
};
Even if read() itself was a template, you must still specialize its class, before you can specialize a template class's template member.
Of course, if your template class has many other members and methods, every one of them have to be specialized here, leading to plenty of code getting duplicated. At that point, you will be faced with several options for refactoring out that duplicated code. The best approach for that depends on the particular details.
But that's how it's done...
EDIT: one common approach is to use a helper template class:
template<typename T> class PropertyValue; // Forward declaration
template<typename T> class do_read {
public:
static int do_it( PropertyValue<T> &me, uint8_t *p )
{
// your default implementation
}
};
template<> class do_read<std::string> {
public:
static int do_it( PropertyValue<std::string> &me, uint8_t *p )
{
// your specialization
}
};
template<typename T>
class PropertyValue
{
T value_;
public:
int read(uint8_t *p)
{
return do_read<T>::do_it(*this, p);
}
};

Variadic template type unpacking into map keys

I'm trying to create a class which will contain a map of type_index keys mapped to pointers of each type passed as a template argument. This would allow me to specify a series of types my class will rely on in it's declaration.
I've done a bit of research but can only seem to find ways to unpack arguments, rather than types. I'm new to this subject, and would appreciate any clarification on terminology, or references to relevant text.
template <typename T>
T* SomeFakeFactoryGetter() { return new T(); }
template <class... Injected>
class UtilityProvider
{
public:
template <class U>
U* GetUtility()
{
std::type_index idx = std::type_index(typeid(U));
assert(_injectedClasses.find(idx) != _injectedClasses.end());
return reinterpret_cast<U*>(_injectedClasses[idx]);
}
// **
// How would I *unpack* all types for use as indices into my map?
// ( I realise this function is not what I want.)
template <Injected... C>
void Unpack()
{
_injectedClasses[std::type_index(typeid(C))] = SomeFakeFactoryGetter<C>();
}
private:
typedef std::unordered_map<std::type_index, void*> InjectedMap;
InjectedMap _injectedClasses;
};
class Bar{ public: void A() { printf("Hello bar"); } };
class Baz{ public: void B() { printf("Hello baz"); } };
class Nope {};
class Foo : public UtilityProvider<Bar, Baz>
{
public:
Foo()
{
GetUtility<Bar>()->A();
GetUtility<Nope>(); // Fail. User must specify which utilities this class will use.
}
};
What I've done in this situation is to create a dummy function to expand these expressions into, but it looks quite hideous:
template <int ... Dummies>
void dummy(int&& ...){}
template <class ... C>
void Unpack()
{
dummy(((_injectedClasses[std::type_index(typeid(C))] =
SomeFakeFactoryGetter<C>()), 0)...);
}
Note that in your case I think you'll be better off with using insert with an initializer_list:
template <class ... C>
void Unpack()
{
_injectedClasses.insert({std::make_pair(std::type_index(typeid(C)),
SomeFakeFactoryGetter<C>())...});
}
I couldn't find a direct mention of this but I believe there is an important difference between the two methods, in case you didn't already know. insert will not override existing key-value pairs, whereas operator[] will. This can affect which method you should use if if this is important to you.
An alternative approach:
template <typename ... C> struct Unpacker;
template <typename Tail, typename ... Queue>
struct Unpacker<Tail, Queue...>
{
void operator () (InjectedMap& injectedClasses) const
{
_injectedClasses[std::type_index(typeid(Tail))] = SomeFakeFactoryGetter<Tail>();
Unpacker<Queue...>()(injectedClasses);
}
};
template <>
struct Unpacker<>
{
void operator () (InjectedMap& injectedClasses) const {}
};

Converting dataset to any stl container

I have some data sets of known types that I'd like to convert into stl
containers by specifying the desired types like this:
Dataset dataset0(....);
Dataset dataset1(....);
Dataset dataset2(....);
int i = dataset0.convert<int>();
vector<int> vi = dataset1.convert<vector<int> >();
pair<vector<bool>, pair<bool, float> > complex = dataset2.convert<vector<pair<vector<bool>, pair<bool, float> >> >();
therefor I have made the following function in my Dataset class:
class Dataset
{
....
template<typename T>
T convert();
};
It's trivial to implement for the basic types like this:
template<>
int convert<int>()
{
return int(/*implementation*/);
}
But the question is, how can I do this for stl containers of any type.
Something like this:
template<>
vector<T> convert<vector<T> >() // ?? does not compile. where would T come from?
{
vector<T> ret;
for(/* Loop determined from data */)
{
ret .push_back(convert<T>())
}
return ret;
}
One would be tempted to simply say
template<typename T>
vector<T> convert<vector<T> >()
This however would mean that you are trying to define a partial specialization of a function template. This is not allowed in C++.
There's no easy work around. The only advice I can give is naming different variants of convert differently (convert_to_vector etc). If you wish to keep naming consistency, you can define a class template Convertor and specialize it for different types, using different convertor functions. For instance:
class Dataset
{
template<typename T>
T convert();
template<typename T>
vector<T> convert_to_vector() { return vector<T>(); }
template<typename T>
list<T> convert_to_list() { return list<T>(); }
};
template<>
int Dataset::convert<int>() { return 0; }
template<>
double Dataset::convert<double>() { return 0; }
template <typename T> class Convertor
{
public: T convert (Dataset& d) { return d.convert<int>(); }
};
template <typename T> class Convertor< vector<T> >
{
public: vector<T> convert (Dataset& d) { return d.convert_to_vector<T>(); }
};
template <typename T> class Convertor< list<T> >
{
public: list<T> convert (Dataset& d) { return d.convert_to_list<T>(); }
};
Then use it like Convertor<some-type>.convert(data).
Maybe this would do
template<typename T>
vector<T> convert();
if you mentionned vectoras an example and want to use other type of containers also, then you should define a `container_type convert_to_container_type()' method for each of them