Linked list with compile time checks in C++ - c++

I am trying to implement a linked list with compile time checks for in and out capabilities. These capabilities (or caps) shall provide information wether a valid linking can take place.
Therefore the out caps of an element are intersected with the in caps of the next element. If a valid intersection can be found, the link is established. Please see provided code for such kind of check at runtime.
While this is rather easy to accomplish at runtime, i do not know the direction for meta/template programming to handle it at compile time.
I already started reading into std::variant<>, std::visit<>, template instantiation, template specialization.
Basically, i know about meta/template programming, but only on a pre C++11 level. Please let me ask this open question, how and if this is feasible with meta/template programming.
Thanks in advance.
#include <assert.h>
#include <list>
enum class NodeType {
Type1,
Type2,
Type3
};
class Caps {
public:
NodeType type;
int minValue;
int maxValue;
};
bool intersect(const std::list<Caps>& in, const std::list<Caps>& out)
{
for (auto i : in) {
for (auto o : out) {
if (i.type == o.type) {
auto minValue = std::max(i.minValue, o.minValue);
auto maxValue = std::min(i.maxValue, o.maxValue);
if (maxValue >= minValue) {
return true;
}
}
}
}
return false;
}
class Node
{
public:
virtual bool link(Node& next) {
if (intersect(this->outCaps(), next.inCaps())) {
m_next = &next;
return true;
}
return false;
}
virtual std::list<Caps> inCaps() { return {}; }
virtual std::list<Caps> outCaps() { return {}; }
protected:
Node* m_next = nullptr;
};
class DerivedNode1 : public Node
{
public:
std::list<Caps> outCaps() override {
return { { NodeType::Type1, 1, 10 },
{ NodeType::Type2, 5, 20 } };
}
};
class DerivedNode2 : public Node
{
std::list<Caps> inCaps() override { return { { NodeType::Type1, 8, 12 } }; }
};
class DerivedNode3 : public Node
{
std::list<Caps> inCaps() override { return { { NodeType::Type2, 1, 4 } }; }
};
class DerivedNode4 : public Node
{
std::list<Caps> inCaps() override { return { { NodeType::Type3, 1, 99 } }; }
};
int main()
{
DerivedNode1 node1;
DerivedNode2 node2;
DerivedNode3 node3;
DerivedNode4 node4;
assert(node1.link(node2)); // This shall link due to matching types and ranges
assert(!node1.link(node3)); // This shall fail due to non-matching range for a specific type
assert(!node1.link(node4)); // This shall fail due to non-matching types
}

The below does what you presumably want. If you don't like the Link class, Node::link2 could be renamed to Node::link, but otherwise it needs to have a different name.
You can use either Node::link2(a, b) or a.link(b) syntax depending on what you prefer. The former doesn't need injection of the single-argument link method to derived classes, so may be preferable. The latter requires a bit more work for deep derivation to work
// Wouldn't work because `DerivedNodeY::link` and `DerivedNodeX::link` are ambiguous;
class DerivedNodeX : public DerivedNodeY, Link<DerivedNodeX>
{
public:
static constexpr std::array<Caps,1> inCaps() { ... }
// but this makes it work:
using Link<DerivedNodeX>::link;
};
Without the Link class, a derived node looks simply like:
class DerivedNode : public Node
{
public:
static constexpr std::array<Caps,1> inCaps() {
return {{ { NodeType::Type3, 1, 99 } }};
}
};
The code is C++17, it compiles with gcc and clang, but it crashes MSVC (up to 19.22) with an internal error :(. Kudos for writing a nice compiler testcase!
#include <array>
#include <type_traits>
enum class NodeType {
Type1,
Type2,
Type3
};
struct Caps {
NodeType type;
int minValue;
int maxValue;
};
class Node
{
public:
static constexpr std::array<Caps,0> inCaps() { return {}; }
static constexpr std::array<Caps,0> outCaps() { return {}; }
template <class InCaps, class OutCaps>
static constexpr bool intersect(const InCaps &in, const OutCaps &out);
template <class N1, class N2>
static std::enable_if_t<
std::is_base_of_v<Node, N1> &&
std::is_base_of_v<Node, N2> &&
intersect(N1::outCaps(), N2::inCaps()), void>
link2(N1 &prev, N2 &next) {
prev.m_next = &next;
}
protected:
Node* m_next = nullptr;
};
template <typename ThisNode>
class Link
{
public:
template <class N2> void link(N2 &next) {
Node::link2(*static_cast<ThisNode*>(this), next);
}
};
template <class InCaps, class OutCaps>
constexpr bool Node::intersect(const InCaps &in, const OutCaps &out)
{
for (auto i : in) {
for (auto o : out) {
if (i.type == o.type) {
auto minValue = std::max(i.minValue, o.minValue);
auto maxValue = std::min(i.maxValue, o.maxValue);
if (maxValue >= minValue) {
return true;
}
}
}
}
return false;
}
class DerivedNode1 : public Node, public Link<DerivedNode1>
{
public:
static constexpr std::array<Caps,2> outCaps() {
return {{ { NodeType::Type1, 1, 10 },
{ NodeType::Type2, 5, 20 } }};
}
};
class DerivedNode2 : public Node, public Link<DerivedNode2>
{
public:
static constexpr std::array<Caps,1> inCaps() {
return {{ { NodeType::Type1, 8, 12 } }};
}
};
class DerivedNode3 : public Node, public Link<DerivedNode3>
{
public:
static constexpr std::array<Caps,1> inCaps() {
return {{ { NodeType::Type2, 1, 4 } }};
}
};
class DerivedNode4 : public Node, public Link<DerivedNode4>
{
public:
static constexpr std::array<Caps,1> inCaps() {
return {{ { NodeType::Type3, 1, 99 } }};
}
};
int main()
{
DerivedNode1 node1;
DerivedNode2 node2;
DerivedNode3 node3;
DerivedNode4 node4;
Node::link2(node1, node2); // compiles
node1.link(node2);
#if 0
Node::link2(node1, node3); // fails to compile
node1.link(node3);
Node::link2(node1, node4); // fails to compile
node1.link(node3);
#endif
}

C++ doesn't have proper intersection types. But you can fake it a bit with compile-time template logic.
Say I define some capabilities as mixins:
struct cap_a {
void foo();
};
struct cap_b {
void bar();
};
struct cap_c {
void baz();
};
And a container like this that inherits from all the capabilities:
template<typename... TCaps>
struct intersectable : public TCaps... {};
I can create an intersection of two intersectable's with a mix of SFINAE and variadic templates. The resulting type only has capabilities that both inputs have.
First step of doing a cross-join is to check if a type is in a list. This is a pretty basic variadic macro. Each iteration pops off a type, checks if its the target type, and or's it with the next iteration.
template<typename TCap, typename... TCaps>
struct has_capability_impl;
template<typename TCap, typename TCapFirst, typename... TCaps>
struct has_capability_impl<TCap, TCapFirst, TCaps...> {
static constexpr bool value = std::is_convertible<TCap, TCapFirst>::value || has_capability_impl<TCap, TCaps...>::value;
};
template<typename TCap>
struct has_capability_impl<TCap> {
static constexpr bool value = false;
};
template<typename TCap, typename... TCaps>
constexpr bool has_capability = has_capability_impl<TCap, TCaps...>::value;
You can do this with fold expressions in C++17, but this works in older versions.
Next is the intersection. This a template with 3 types: An empty result itersectable, the left hand side, and branches with enable_if. If the type is present on the right intersectable, move the type over to the result in the inherited base type. OTherwise, don't.
For each iteration, pop a type off a
template<typename TLRhs, typename TLhs, typename TRhs, typename = void>
struct intersect_impl;
template<typename... TLRCaps, typename TFirst, typename... TLCaps, typename... TRCaps>
struct intersect_impl<intersectable<TLRCaps...>, intersectable<TFirst, TLCaps...>, intersectable<TRCaps...>, std::enable_if_t<has_capability<TFirst, TRCaps...>>> : intersect_impl<intersectable<TLRCaps..., TFirst>, intersectable<TLCaps...>, intersectable<TRCaps...>> { };
template<typename... TLRCaps, typename TFirst, typename... TLCaps, typename... TRCaps>
struct intersect_impl<intersectable<TLRCaps...>, intersectable<TFirst, TLCaps...>, intersectable<TRCaps...>, std::enable_if_t<!has_capability<TFirst, TRCaps...>>> : intersect_impl<intersectable<TLRCaps...>, intersectable<TLCaps...>, intersectable<TRCaps...>> { };
By the time there are no types left on the left hand side, the result only has capabilities present from both sides:
template<typename... TLRCaps, typename... TRCaps>
struct intersect_impl<intersectable<TLRCaps...>, intersectable<>, intersectable<TRCaps...>> {
using type = intersectable<TLRCaps...>;
};
template<typename TLhs, typename TRhs>
using intersection = typename intersect_impl<intersectable<>, TLhs, TRhs, void>::type;
Package that in a trivial function to combine instances:
template<typename TLhs, typename TRhs>
intersection<TLhs, TRhs> intersect(TLhs lhs, TRhs rhs) {
return intersection<TLhs, TRhs>{}; // runtime link logic goes here
}
...and the result is type-safe capability intersections:
int main() {
intersectable<cap_a, cap_c> ac;
ac.foo();
ac.baz();
intersectable<cap_a, cap_b> ab;
ab.foo();
ab.bar();
auto a = intersect(ac, ab);
a.foo();
a.bar(); // error
a.baz(); // error
}
Demo: https://godbolt.org/z/-kd2Qj
Again, this doesn't actually do anything, it just intersects the types. But you can use something like this to make your linked list logic type-safe.
Anyway, to add range checking, it's just a matter of working in compile-time properties for range into the definition of intersectable
#include <cstdint>
#include <functional>
#include <type_traits>
#include <unordered_set>
struct cap_a {
void foo();
};
struct cap_b {
void bar();
};
struct cap_c {
void baz();
};
template<int Min, int Max, typename... TCaps>
struct intersectable : public TCaps... {
};
template<typename TCap, typename... TCaps>
struct has_capability_impl;
template<typename TCap, typename TCapFirst, typename... TCaps>
struct has_capability_impl<TCap, TCapFirst, TCaps...> {
static constexpr bool value = std::is_convertible<TCap, TCapFirst>::value || has_capability_impl<TCap, TCaps...>::value;
};
template<typename TCap>
struct has_capability_impl<TCap> {
static constexpr bool value = false;
};
template<typename TCap, typename... TCaps>
constexpr bool has_capability = has_capability_impl<TCap, TCaps...>::value;
template<typename TLRhs, typename TLhs, typename TRhs, typename = void>
struct intersect_impl;
template<int LRMin, int LRMax, int LMin, int LMax, int RMin, int RMax, typename... TLRCaps, typename TFirst, typename... TLCaps, typename... TRCaps>
struct intersect_impl<
intersectable<LRMin, LRMax, TLRCaps...>,
intersectable<LMin, LMax, TFirst, TLCaps...>,
intersectable<RMin, RMax, TRCaps...>,
std::enable_if_t<(has_capability<TFirst, TRCaps...>)>>
: intersect_impl<
intersectable<LRMin, LRMax, TLRCaps..., TFirst>,
intersectable<LMin, LMax, TLCaps...>,
intersectable<RMin, RMax, TRCaps...>> { };
template<int LRMin, int LRMax, int LMin, int LMax, int RMin, int RMax, typename... TLRCaps, typename TFirst, typename... TLCaps, typename... TRCaps>
struct intersect_impl<
intersectable<LRMin, LRMax, TLRCaps...>,
intersectable<LMin, LMax, TFirst, TLCaps...>,
intersectable<RMin, RMax, TRCaps...>,
std::enable_if_t<(!has_capability<TFirst, TRCaps...>)>>
: intersect_impl<
intersectable<LRMin, LRMax, TLRCaps...>,
intersectable<LMin, LMax, TLCaps...>,
intersectable<RMin, RMax, TRCaps...>> { };
template<int LMin, int LMax, int RMin, int RMax, typename TResult, typename... TRCaps>
struct intersect_impl<TResult, intersectable<LMin, LMax>, intersectable<RMin, RMax, TRCaps...>> {
using type = TResult;
};
template <typename T>
struct props;
template<int Min, int Max, typename... Caps>
struct props<intersectable<Min, Max, Caps...>> {
static constexpr int min = Min;
static constexpr int max = Max;
};
template<typename TLhs, typename TRhs>
using intersection = typename intersect_impl<
intersectable<(std::max(props<TLhs>::min, props<TRhs>::min)), (std::min(props<TLhs>::max, props<TRhs>::max))>,
TLhs,
TRhs>::type;
template<typename TLhs, typename TRhs>
intersection<TLhs, TRhs> intersect(TLhs lhs, TRhs rhs) {
static_assert((props<TLhs>::max >= props<TRhs>::min && props<TLhs>::min <= props<TRhs>::max) ||
(props<TRhs>::max >= props<TLhs>::min && props<TRhs>::min <= props<TLhs>::max), "out of range");
return intersection<TLhs, TRhs>{}; // runtime intersection logic?
}
int main() {
intersectable<1, 5, cap_a, cap_c> ac;
ac.foo();
ac.baz();
intersectable<3, 8, cap_a, cap_b> ab;
ab.foo();
ab.bar();
auto a = intersect(ac, ab); // result is a intersectable<3, 5, cap_a>
a.foo();
a.bar(); // error
a.baz(); // error
intersectable<10, 15, cap_a, cap_b> ab_out_of_range;
auto a0 = intersect(ac, ab_out_of_range);
}
demo: https://godbolt.org/z/zz9-Vg

Related

Need a way to check template type and if not in the range of allowed types then have compile time error in C++

I need to have a template class where each object adds itself to a vector and based on the template type parameter(allowed only: string, int, float) add to the corresponding container. I need a way to have compile time checks for the type and based on the check add to the corresponding container and if the type is not one of the allowed types compile time error should be emitted.
Example: code
vector<myClass<int>*> intVec;
vector<myClass<float>*> floatVec;
vector<myClass<string>*> stringVec;
template<typename T>
struct myClass
{
myClass()
{
/*pseudo code
if(T == int) {
intVec.push_back(this);
}
else if(T == float) {
floatVec.push_back(this);
}
else if(T == string){
stringVec.push_back(this);
}
else {
// error
}
*/
}
T value;
}
How can I achieve this ?
In C++17 and later, you can use if constexpr and std::is_same_v, eg:
#include <type_traits>
template<typename T>
struct myClass
{
myClass()
{
if constexpr (std::is_same_v<T, int>) {
m_intVec.push_back(this);
}
else if constexpr (std::is_same_v<T, float>) {
m_floatVec.push_back(this);
}
else if constexpr (std::is_same_v<T, std::string>){
m_stringVec.push_back(this);
}
else {
// error
}
}
T value;
};
In earlier versions, you can use either template specialization or SFINAE instead, eg:
// via specialization
template<typename T>
struct myClass
{
};
template<>
struct myClass<int>
{
myClass()
{
m_intVec.push_back(this);
}
int value;
};
template<>
struct myClass<float>
{
myClass()
{
m_floatVec.push_back(this);
}
float value;
};
template<>
struct myClass<std::string>
{
myClass()
{
m_stringVec.push_back(this);
}
std::string value;
};
// via SFINAE
#include <type_traits>
template<typename T>
struct myClass
{
template<typename U = T, std::enable_if<std::is_same<U, int>::value, int>::type = 0>
myClass()
{
m_intVec.push_back(this);
}
template<typename U = T, std::enable_if<std::is_same<U, float>::value, int>::type = 0>
myClass()
{
m_floatVec.push_back(this);
}
template<typename U = T, std::enable_if<std::is_same<U, std::string>::value, int>::type = 0>
myClass()
{
m_stringVec.push_back(this);
}
T value;
};
Use specialization and a helper function, e.g.
template<typename T>
struct myClass;
inline std::vector<myClass<int>*> intVec;
inline std::vector<myClass<float>*> floatVec;
inline std::vector<myClass<std::string>*> stringVec;
template<typename T>
void add(myClass<T>*);
template<>
void add(myClass<int>* p) {
intVec.push_back(p);
}
template<>
void add(myClass<float>* p) {
floatVec.push_back(p);
}
template<>
void add(myClass<std::string>* p) {
stringVec.push_back(p);
}
template<typename T>
struct myClass
{
myClass()
{
add(this);
}
T value;
};
in addition to existing answers, you can also do it with normal function overloading
template<typename T>
struct myClass;
inline std::vector<myClass<int>*> intVec;
inline std::vector<myClass<float>*> floatVec;
inline std::vector<myClass<std::string>*> stringVec;
/* optional
template<typename T>
constexpr bool always_false = false;
template<typename T>
void add(myClass<T>*) {
static_assert(always_false<T>,"unsupported T");
}
*/
void add(myClass<int>* p) {
intVec.push_back(p);
}
void add(myClass<float>* p) {
floatVec.push_back(p);
}
void add(myClass<std::string>* p) {
stringVec.push_back(p);
}
template<typename T>
struct myClass
{
myClass()
{
add(this);
}
T value;
};

C++ template parameter: to find out if a parameter exists in parameter list during compilation time

I have a struct Robot:
template<typename... FeatureList>
struct Robot {
Robot() = default;
};
That it can be configured with a few features (a few structs are used as token here):
struct CanWalk {
};
struct CanNotWalk {
};
struct CanFly {
};
struct CanNotFly {
};
Robot<CanWalk, CanFly> robot_A = Robot<CanWalk, CanFly>();
Then I have no idea how to implement a bool function isConfiguredWith() within struct Robot that tells me if a feature is encoded into the struct:
template<typename... FeatureList>
template<typename Feature>
constexpr bool Robot<FeatureList...>::isConfiguredWith() {
// how do I implement it?
return false;
}
int main() {
Robot<CanWalk, CanFly> robot_A = Robot<CanWalk, CanFly>();
static_assert(robot_A.isConfiguredWith<CanWalk>());
static_assert(robot_A.isConfiguredWith<CanFly>());
return 0;
}
How do I loop through FeatureList to find out if Feature exist in the lists, in compilation time?
Full code:
struct CanWalk {
};
struct CanNotWalk {
};
struct CanFly {
};
struct CanNotFly {
};
template<typename... FeatureList>
struct Robot {
Robot() = default;
template<typename Feature>
constexpr bool isConfiguredWith();
};
template<typename... FeatureList>
template<typename Feature>
constexpr bool Robot<FeatureList...>::isConfiguredWith() {
return false;
}
int main() {
Robot<CanWalk, CanFly> robot_A = Robot<CanWalk, CanFly>();
static_assert(robot_A.isConfiguredWith<CanWalk>());
static_assert(robot_A.isConfiguredWith<CanFly>());
return 0;
}
You could add a feature test type trait:
#include <type_traits>
template<class Feature, class... FeatureList>
struct has_feature {
static constexpr bool value = (std::is_same_v<Feature, FeatureList> || ...);
};
template<class Feature, class... FeatureList>
inline constexpr bool has_feature_v = has_feature<Feature,FeatureList...>::value;
And use it in your Robot class like this:
template<class... FeatureList>
struct Robot {
Robot() = default;
template<class Feature>
static constexpr bool isConfiguredWith() {
return has_feature_v<Feature,FeatureList...>;
}
};
Demo
(std::is_same_v<Feature, FeatureList> || ...) is a fold expression.
Say FeatureList... is CanWalk, CanFly. The above fold expression then "unfolds" to:
std::is_same_v<Feature, CanWalk> || std::is_same_v<Feature, CanFly>
You can also investigate these template instantiations in more detail at cppinsights.io
You can write a trait like this:
#include <type_traits>
#include <iostream>
template<typename... FeatureList>
struct Robot {
Robot() = default;
};
struct CanWalk {};
struct CanNotWalk {};
struct CanFly {};
struct CanNotFly {};
template <typename Feature,typename... Features>
using has_feature_impl = std::integral_constant<bool,(std::is_same_v<Feature,Features> || ...)>;
template <typename Feature,typename T> struct has_feature;
template <typename Feature,typename... Features> struct has_feature<Feature,Robot<Features...>> : has_feature_impl<Feature,Features...> {};
int main()
{
using R1 = Robot<CanWalk,CanFly>;
std::cout << has_feature<CanWalk,R1>::value;
std::cout << has_feature<CanNotFly,R1>::value;
}
Output:
10

Hold multiple maps having different value types in a class and support type based lookup

I want to implement a heterogeneous storage, using multiple maps having different value type. I am indexing the maps based on the value types. For that I am using typelist. This is a simplified example.
My type-list class looks like following.
namespace details
{
struct null{};
template<int N, typename E, typename T, typename ... Ts>
struct index : index<N+1, E, Ts...> {};
template<int N, typename E, typename ... Ts>
struct index<N, E, E, Ts...>
{
constexpr static int value = N;
};
template<int N, typename E>
struct index<N, E, null>
{
constexpr static int value = -1;
};
}
template<typename ... Ts>
struct typelist
{
using type = typelist<Ts...>;
template<typename T>
struct index : details::index<0, T, Ts..., details::null> {};
};
And storage class implementation following, A template class holding values of type T.
#include <iostream>
#include <tuple>
#include <typeinfo>
#include <map>
template<typename T>
struct storage_impl
{
//storage_impl(storage_impl const &) = delete;
bool try_getting(int key, T &value)
{
auto search = _storage.find(key);
if(search != _storage.end() )
{
value = search->second;
return true;
}
return false;
}
std::map<int,T> _storage;
};
And finally the storage manager looks like, it has several storage_impl instance, and I want to index on them based on type.
struct storage_manager{
// tuple storing different storage_impls
std::tuple< storage_impl<double> , storage_impl<std::string> > storages { storage_impl<double>{},storage_impl<std::string>{} };
using item_types = typelist<double,std::string>;
storage_manager(){}
~storage_manager(){}
storage_manager(storage_manager const &) = delete;
template<typename T>
bool try_getting(int key, T &value)
{
return std::get<item_types::index<T>::value>(storages).try_getting(key,value);
}
};
int main()
{
storage_manager mgr;
double val1;
std::cout<<mgr.try_getting(123,val1);
}
Every thing works till the storage_impl is copyable. But I want storage_impl to be non-copyable.
How to achieve same with my storage_impl being non-copyable.
P.S. - Did not want to use inheritance for same.
Following is my working code, I have declared cache_impls as member variables and specialized function storage_ref for every case.
I have implemented only try_get, other functions can be implemented using storage_ref.
#include <iostream>
#include <tuple>
#include <map>
#include <boost/noncopyable.hpp>
template<typename T>
struct storage_impl : public boost::noncopyable
{
storage_impl(){}
~storage_impl(){}
bool try_getting(int key, T &value)
{
auto search = _storage.find(key);
if(search != _storage.end() )
{
value = search->second;
return true;
}
return false;
}
bool try_setting(int key, T const &value)
{
auto search = _storage.insert(std::make_pair(key,value));
if(search.second == true )
{
return true;
}
return false;
}
bool exists(int key)
{
auto search = _storage.find(key);
if(search != _storage.end() )
{
return true;
}
return false;
}
std::map<int,T> _storage;
};
struct storage_manager : public boost::noncopyable
{
storage_impl<double> double_store;
storage_impl<std::string> string_store;
template<typename T>
struct item_return{ using type = storage_impl<T>; };
template<typename T>
typename item_return<T>::type storage_ref();
template<typename T>
bool try_getting(int key, T &value)
{
return storage_ref<T>().try_getting(key,value);
}
template<typename T>
bool try_setting(int key, T const &value)
{
return storage_ref<T>().try_setting(key,value);
}
template<typename T>
bool exists(int key)
{
return storage_ref<T>().exists(key);
}
storage_manager(){}
~storage_manager(){}
};
//double specialization
template<>
struct storage_manager::item_return<double>{ using type = storage_impl<double>&; };
template<>
inline storage_impl<double>& storage_manager::storage_ref<double>()
{
return double_store;
}
//std::string specialization
template<>
struct storage_manager::item_return<std::string>{ using type = storage_impl<std::string>&; };
template<>
inline storage_impl<std::string>& storage_manager::storage_ref<std::string>()
{
return string_store;
}
int main()
{
storage_manager mgr;
double val1 = 90;
std::cout<<mgr.try_getting(123,val1)<<'\n';
std::cout<<mgr.try_setting(123,val1)<<'\n';
std::cout<<mgr.exists<double>(123)<<'\n';
std::string val2;
std::cout<<mgr.try_getting(123,val2)<<'\n';
}
Looking forward for alternatives...

accessing members of a templated class with variable numbers of templated arguments

I have a templated class with variable numbers of templated arguments. As in these cases (I cannot afford C++11) a good practice is to create a default class that we call none and put it as default like below.
struct none {};
template<class T1=none, T2=none, T3=none>
class A{
template<class T>
double extract() { return none();}
template<>
double extract<T1>() { return m1_();}
template<>
double extract<T2>() { return m2_();}
template<>
double extract<T3> () { return m3_();}
T1 m1_;
T2 m2_;
T3 m3_;
};
At this stage I don't know how to implement a generic/templated accessor function that can access each of the templated argument.
All of the templated arguments are different so I specialized A::extract() for each of the templated arguments.
Is there any better way to do this? Any sort of tagging I can have a look at?
struct none {};
template <class T, class N>
class Holder : public N
{
protected:
T m;
typedef Holder<T, N> First;
double extractP(T*) { return m(); }
template <class X> double extractP(X*) {
return this->N::extractP(static_cast<X*>(0));
}
};
template <class T>
class Holder<T, none>
{
protected:
T m;
typedef Holder<T, none> First;
double extractP(T*) { return m(); }
template <class X> none extractP(X*) {
return none();
}
};
template <class T1 = none, class T2 = none, class T3 = none>
class A : Holder<T1, Holder<T2, Holder<T3, none> > >
{
public:
template <class T> double extract() {
return this->extractP(static_cast<T*>(0));
}
};
A similarly-named solution to n.m but more on the Boost's Variant class design.
The suggestion is to use a Variant container (a generic container for your objects) and use accessors directly on them.
#include <iostream>
#include <stdexcept>
using namespace std;
class BaseHolder
{
public:
virtual ~BaseHolder(){}
virtual BaseHolder* clone() const = 0;
};
template<typename T>
class HoldData : public BaseHolder
{
public:
HoldData(const T& t_) : t(t_){}
virtual BaseHolder* clone() const {
return new HoldData<T>(t);
}
T getData() {
return t;
}
private:
T t;
};
class Variant
{
public:
Variant() : data(0) {}
template<typename T>
Variant(const T& t) : data(new HoldData<T>(t)){}
Variant(const Variant& other) : data(other.data ? other.data->clone() : 0) {}
~Variant(){delete data;}
template<typename T>
T getData() {
return ((HoldData<T>*)data)->getData();
}
private:
BaseHolder* data;
private:
Variant& operator=(const Variant& other) { return *this;} // Not allowed
};
struct none {};
class Container{
public:
Container() : m1_(0), m2_(0), m3_(0){}
~Container() {
if(m1_)
delete m1_;
if(m2_)
delete m1_;
if(m3_)
delete m1_;
}
none extract() { return none();}
template<typename T>
void insertM1(T obj) {
m1_ = new Variant(obj);
}
template<typename T>
T extractM1() {
if(m1_ != 0)
return m1_->getData<T>();
else
throw std::runtime_error("Element not set");
}
// TODO: implement m2 and m3
Variant *m1_;
Variant *m2_;
Variant *m3_;
};
int main() {
Container obj;
char M1 = 'Z';
obj.insertM1(M1);
char extractedM1 = obj.extractM1<char>();
cout << extractedM1;
return 0;
}
http://ideone.com/BaCWSV
Your class seems to mimic std::tuple, which, unfortunately for you, was added in C++11. The good news is that you can use boost::tuple instead.
As an example of usage:
boost::tuple<std::string, double> t = boost::make_tuple("John Doe", 4.815162342);
std::cout << boost::get<0>(t) << '\n';
std::cout << boost::get<1>(t) << '\n';
Live demo
Without access to C++11, it's a bit uglier, but you can leverage Boost.Tuple:
#include <iostream>
#include <boost/tuple/tuple.hpp>
template <size_t I, typename T, typename U>
struct AccessImpl;
template <size_t I, typename T, typename U>
struct AccessImpl {
template <typename Tuple>
static T& impl(Tuple& tuple) {
typedef typename ::boost::tuples::element<I+1, Tuple>::type Next;
return AccessImpl<I+1, T, Next>::impl(tuple);
}
};
template <size_t I, typename T>
struct AccessImpl<I, T, T> {
template <typename Tuple>
static T& impl(Tuple& tuple) { return boost::get<I>(tuple); }
};
template <typename T, typename Tuple>
T& access(Tuple& tuple) {
typedef typename ::boost::tuples::element<0, Tuple>::type Head;
return AccessImpl<0, T, Head>::impl(tuple);
}
int main() {
boost::tuples::tuple<char, int, std::string> example('a', 1, "Hello, World!");
std::cout << access<std::string>(example) << "\n";
return 0;
}
This, as expected, prints "Hello, World!".

Select C++ template specialization based on member type availability

I am writing a sort of serialization class. It must provide functions for containers. Current implementation is:
template <typename InsertIter>
bool readContainer(InsertIter result)
{
typedef typename InsertIter::container_type::value_type tVal;
UInt32 size = 0;
if (!read(size))
return false;
for (UInt32 i = 0; i < size; ++i)
{
tVal val;
if (!read(val))
return false;
*result++ = val;
}
return true;
}
template <typename InsertIter>
bool readMap(InsertIter result)
{
typedef typename InsertIter::container_type::key_type tKey;
typedef typename InsertIter::container_type::mapped_type tVal;
UInt32 size = 0;
if (!read(size))
return false;
for (UInt32 i = 0; i < size; ++i)
{
std::pair<tKey, tVal> pair;
if (!read(pair))
return false;
*result++ = pair;
}
return true;
}
As you can see, I must to create different implementations for map-like types (std::map) and other containers, because std::map::value_type is std::pair(const K, V) and not std::pair(K, V).
So, I want to create method read(InsertIter) which will automatically select appropriate readContainer(InsertIter) or readMap(InsertIter). Is this possible?
I have an example that does something very similar, and it should be very simple for you to convert to something that you need :
#include <iostream>
template< typename T >
struct A;
template<>
struct A< int >
{
void foo() const
{
std::cout<<"A(int)::foo()"<<std::endl;
}
};
template<>
struct A< float >
{
void foo() const
{
std::cout<<"A(float)::foo()"<<std::endl;
}
};
template< typename T >
void call( const A<T> &a)
{
a.foo();
}
struct B
{
template<typename T>
void bar(const A<T> &a)
{
call(a);
}
};
int main()
{
A<int> a1;
A<float> a2;
B b;
b.bar(a1);
b.bar(a2);
}
You need to give proper names, and replace int and float in the above example with appropriate container types, and implement needed functionality.
I have successfully solved my problem.
Thanks to Johan Lundberg and especially to n.m. - I was not familiar with SFINAE idiom and your links and samples helped me a lot.
I was not able to use C++11 features (project specifics), but they are not needed.
Current code looks like this:
struct SFINAE
{
typedef char __one;
typedef struct { char __arr[2]; } __two;
};
template <typename T>
class has_mapped_type : public SFINAE
{
template <typename C> static __one test(typename C::mapped_type *);
template <typename C> static __two test(...);
public:
enum { value = (sizeof(test<T>(0)) == sizeof(__one)) };
};
class Serializer
{
template <typename InsertIter>
bool read(InsertIter result) const
{
return readContainerSelector< InsertIter,
has_mapped_type<typename InsertIter::container_type>::value
> ::read(result, *this);
}
template <typename InsertIter, bool isMapType>
struct readContainerSelector;
template <typename InsertIter>
struct readContainerSelector<InsertIter, true>
{
static bool read(InsertIter result, Serializer const& ser)
{
return ser.readMap(result);
}
};
template <typename InsertIter>
struct readContainerSelector<InsertIter, false>
{
static bool read(InsertIter result, Serializer const& ser)
{
return ser.readContainer(result);
}
};
// methods from my topic post
template <typename InsertIter> bool readContainer(InsertIter result);
template <typename InsertIter> bool readMap(InsertIter result)
};