Related
I have a vector of pointers to Base.
Invariant: only one of each derived type should be in that vector at any time.
I also want to be able to lookup the value with a given type in O(1). I can do this in O(n) easily, by checking dynamic_cast.
Basically, I want to replace my vector with a map or something. Is that possible?
Here's minimal example with the vector and the loop:
#include <functional>
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
using namespace std;
typedef struct Base {
virtual ~Base(){};
} Base;
vector<unique_ptr<Base>> baseList;
template <typename NarrowType,
typename std::enable_if_t<
! std::is_same_v<Base, NarrowType> &&
std::is_base_of_v<Base, NarrowType>,
bool> = true>
void ApplyFuncToType(function<void(NarrowType)> func) {
// Want to get rid of this loop
for (auto &base : baseList) {
NarrowType *narrow = dynamic_cast<NarrowType *>(base.get());
if (narrow) {
func(*narrow);
}
}
}
// usage
int main() {
typedef struct A : Base {
void printA() { cout << "a" << endl; }
} A;
typedef struct B : Base {
void printB() { cout << "b" << endl; }
} B;
baseList.push_back(make_unique<A>());
baseList.push_back(make_unique<B>());
ApplyFuncToType<A>([](A a) { a.printA(); });
}
Questions:
How can I enfore my invariant (one of each type max in container)
Would a unordered_map<type_info, unique_ptr<Base>> be a good solution to this? I have read that typeid is not consistent or safe to use or something, but am not sure exactly.
Edits/Clarification:
This is for a system where other classes can register their own types in this vector. i.e. the contents of the vector will change during runtime.
A similar approach is shown here, where an unordered_map is used to allow self-registered event callbacks.
Yeah, sure, it's possible, but I'm not convinced you need it. After all, all your types are completely static.
Also, ApplyFuncToType shouldn't be taking std::function, but a generic argument, since you'll save on the cost of shoehorning things into std::function. You're not deducing any types anyway - because std::function is not a tool for that - and thus you have the call that includes the type parameter explicitly: ApplyFuncToType<A>.
And finally, it's probably wrong to pass A and B to the lambda by value - since then the instance the lambda is using is not the instance you so carefully deposited beforehand (!). It should be passed by const reference, or by reference if it's a non-const method:
// Do this
ApplyFuncToType<A>([](const A &a) { a.printA(); });
// Or do that
ApplyFuncToType<A>([](A &a) { a.printA(); });
// NO!
ApplyFuncToType<A>([](A a) { a.printA(); });
It's hard to deduce it ahead of time, but I imagine that you'd want to make A, B, ... non-copyable but they definitely should be movable (read on).
A Tuple of Pointers
All you really want is the below - and it doesn't care that the types are derived from some base, you can use any types you wish. You can of course add type constraints if you want to protect from bugs where wrong types are supplied to ptr_tuple.
#include <functional>
#include <memory>
#include <tuple>
struct A { void methodA() {} };
struct B { void methodB() {} };
template <class ...Args>
using ptr_tuple = std::tuple<std::unique_ptr<Args>...>;
ptr_tuple<A, B> instances;
template <typename T>
auto &instance()
{
return std::get<std::unique_ptr<T>>(instances);
}
template <class T, class Fun, class ...Args>
void invoke(Fun &&fun, Args &&...args)
{
auto *ptr = instance<T>().get();
if (ptr) {
std::invoke(fun, *ptr, std::forward<Args>(args)...);
}
}
int main() {
instance<A>() = std::make_unique<A>();
instance<B>() = std::make_unique<B>();
invoke<A>([](A& a){ a.methodA(); });
invoke<B>([](B& b){ b.methodB(); });
}
Argument Deduction for Invoke/Apply
It's not even necessary to supply the explicit type parameter to invoke. We can deduce it. For that, we use a traits class that's sorely missing in C++ standard library:
// from https://stackoverflow.com/a/39717241/1329652
// see also
// https://github.com/kennytm/utils/blob/master/traits.hpp
// https://stackoverflow.com/a/27885283/1329652
// boost::callable_traits
template <typename T, typename = void>
struct function_traits;
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using args_type = std::tuple<A... >;
using arg0_class = std::decay_t<std::tuple_element_t<0, args_type>>;
};
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)>
{
using args_type = std::tuple<A... >;
using arg0_class = std::decay_t<std::tuple_element_t<0, args_type>>;
};
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...) const>
{
using args_type = std::tuple<A... >;
using arg0_class = std::decay_t<std::tuple_element_t<0, args_type>>;
};
template <typename T>
struct function_traits<T, std::void_t<decltype(&T::operator())> >
: public function_traits< decltype(&T::operator()) >
{};
And then we can deduce the needed type in invoke:
template <class Fun, class ...Args>
void invoke(Fun &&fun, Args &&...args)
{
using arg0_class = typename function_traits<std::decay_t<Fun>>::arg0_class;
auto *ptr = instance<arg0_class>().get();
if (ptr) {
std::invoke(fun, *ptr, std::forward<Args>(args)...);
}
}
int main() {
instance<A>() = std::make_unique<A>();
instance<B>() = std::make_unique<B>();
invoke([](A& a){ a.methodA(); });
invoke([](B& b){ b.methodB(); });
}
A Tuple of Optional Values
Depending on what your A and B types really are, if they can be moved, then using dynamic memory allocation is totally unnecessary, you'd much rather keep them by value, e.g. with optional:
#include <functional>
#include <memory>
#include <optional>
#include <tuple>
struct A { void methodA() {} };
struct B { void methodB() {} };
template <class ...Args>
using opt_tuple = std::tuple<std::optional<Args>...>;
opt_tuple<A, B> instances;
template <typename T> auto &instance()
{
return std::get<std::optional<T>>(instances);
}
template <class T, class Fun, class ...Args>
void invoke(Fun &&fun, Args &&...args)
{
auto &opt = instance<T>();
if (opt) {
std::invoke(fun, *opt, std::forward<Args>(args)...);
}
}
int main() {
instance<A>().emplace(); // constructs A
instance<B>().emplace(); // constructs B
invoke<A>([](A& a){ a.methodA(); });
invoke<B>([](B& b){ b.methodB(); });
}
Of course you can add the type-deduced variant of invoke just as before.
A type-id Stand In
Even though I really think that your original solution is in want of a problem - you should state what problem you're trying to solve, otherwise it smells of an XY problem - there of course is a better "type id" than type_id: an address of a function templated on a type. There'll be only one instance of it per program.
I don't think that the "O(1)" lookup is a real requirement, a very, very fast O(log(N)) lookup - way faster than you'd get from e.g. std::map, will work just as well for whatever your imaginary applications is.
Thus:
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <vector>
// here goes function_traits implementation from above
struct Base {};
template <typename T>
constexpr static bool is_derived_from_Base_v =
!std::is_same_v<Base, T> && std::is_base_of_v<Base, T>;
class UniqueTypeObjects {
using marker_type = void(*)();
struct Pair {
std::unique_ptr<Base> base;
marker_type marker;
Pair(std::unique_ptr<Base> &&base, marker_type marker) : base(std::move(base)), marker(marker) {}
bool operator<(marker_type o) const { return marker < o; }
};
friend bool operator<(marker_type a, const Pair &o);
template <typename T, typename = std::enable_if<is_derived_from_Base_v<T>>>
struct Witness {
static void marker() {}
};
std::vector<Pair> m_objects;
public:
template <class Derived, class =
std::enable_if_t<is_derived_from_Base_v<Derived>>>
void insert(std::unique_ptr<Derived> &&obj) {
auto constexpr marker = &Witness<Derived>::marker;
auto it = std::lower_bound(m_objects.begin(), m_objects.end(), marker);
if (it != m_objects.end() && it->marker == marker)
throw std::logic_error("Attempting to insert an object of duplicate type");
m_objects.emplace(it, std::move(obj), marker);
}
template <typename Derived, typename Fun,
class = std::enable_if_t<is_derived_from_Base_v<Derived>>>
void apply(Fun fun) const {
auto constexpr marker = &Witness<Derived>::marker;
auto it = std::lower_bound(m_objects.begin(), m_objects.end(), marker);
if (it == m_objects.end() || it->marker != marker)
throw std::runtime_error("No object found to apply the function to");
std::invoke(fun, *static_cast<Derived*>(it->base.get()));
}
template <typename Fun,
class = std::enable_if_t<is_derived_from_Base_v<
typename function_traits<std::decay_t<Fun>>::arg0_class>>>
void apply(Fun fun) const {
using arg0_class = typename function_traits<std::decay_t<Fun>>::arg0_class;
apply<arg0_class>(std::move(fun));
}
};
bool operator<(void(*a)(), const UniqueTypeObjects::Pair &o)
{ return a < o.marker; }
char lastInvoked;
int main() {
struct A : Base {
void methodA() { lastInvoked = 'A'; }
};
struct B : Base {
void methodB() { lastInvoked = 'B'; }
};
UniqueTypeObjects uto;
uto.insert(std::make_unique<A>());
uto.insert(std::make_unique<B>());
assert(!lastInvoked);
uto.apply([](A &a){ a.methodA(); });
assert(lastInvoked == 'A');
uto.apply([](B &b){ b.methodB(); });
assert(lastInvoked == 'B');
}
But I still don't think it's necessary. If you truly have O(1) requirement, e.g. some sort of a realtime system, or system with deterministic execution timing, then the opt_tuple solution or its equivalent is the one you should use. Otherwise - good luck with the paperwork and test plans to ensure that UniqueTypeObjects works. I wrote the thing and even I wouldn't allow it in a realtime or hi-reliability codebase I maintained. Nothing beats static type safety and ensuring correctness by design, and you get that with the tuple approach (or its equivalent with a custom class).
I need to be able to access a static method of the derived class, from within a base CRTP class. Is there a way in which I can achieve this?
Here is example code:
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__), bool> = true
template<typename Derived>
struct ExpressionBase {
Derived& derived() { return static_cast<Derived&>(*this); }
const Derived& derived() const { return static_cast<const Derived&>(*this); }
constexpr static int size()
{
return Derived::size();
}
template<typename T, REQUIRES(size() == 1)>
operator T() const;
};
struct Derived : public ExpressionBase<Derived>
{
constexpr static int size()
{
return 1;
}
};
Deriving from ExpressionBase<Derived> involves the instantiation of ExpressionBase<Derived>, therefore involves the declaration of the entity
template<typename T, REQUIRES(size() == 1)>
operator T() const;
Here, std::enable_if_t got a template argument that is ill-formed (because Derived isn't complete yet). The SFINAE rule does not apply here, because the ill-formed expression is not in direct context of template argument type, thus it is treated as a hard error.
In order to make the ill-formation happen at an immediate context, use the following code:
#include <type_traits>
template <bool B, class T>
struct lazy_enable_if_c {
typedef typename T::type type;
};
template <class T>
struct lazy_enable_if_c<false, T> {};
template <class Cond, class T>
struct lazy_enable_if : public lazy_enable_if_c<Cond::value, T> {};
template <class T>
struct type_wrapper {
using type = T;
};
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__), bool> = true
template<typename Derived>
struct ExpressionBase {
Derived& derived() { return static_cast<Derived&>(*this); }
const Derived& derived() const { return static_cast<const Derived&>(*this); }
struct MyCond {
static constexpr bool value = Derived::size() == 1;
};
template<typename T, typename = typename lazy_enable_if<MyCond, type_wrapper<T>>::type>
operator T () const {
return T{};
}
};
struct Derived : public ExpressionBase<Derived>
{
constexpr static int size() {
return 1;
}
};
int main() {
Derived d;
int i = d;
return 0;
}
It is actually adapted from boost, which you can find more details here.
I am trying to create a filesystem interface so that my micro controller can interface with an SD card (and I decided to implement all of the File System stuff from the ground up). The problem is I don't know what file system will be on the card....It could be FAT16, FAT32, NFTS, ext3, ect.
So I created a the following abstract classes: FileSystem File and Directory. Now that is all fine and dandy but I am on a micro controller so I want to avoid using the new operator.
This leads to my creation of the UnionBase class (not a very helpful name). Basically this class holds a union of all of the different derived classes and allows you to convert between them:
struct BaseFile_t{
};
struct DerivedA : BaseFile_t{
};
struct DerivedB : BaseFile_t{
};
UnionBase<BaseFile_t,DerivedA,DerivedB> var; //Can now pass references
//of this into File system function
//so that they can modify the right
//Derived type (which can then be
//used as if it is the base type)
Now in order to pass this in I have a struct called FileSystemUnion or FSU for short. This basically just defines all of the necessary BaseUnion types.
The real problem is that it seems that it might end up being a type if recursive typedef (which I know is not allowed). Here is a shortened version of my code:
#include <stdio.h>
#include <iostream>
#include <string>
#include <conio.h>
#include <stdlib.h>
#include <fstream>
#include "prototypeInd/templates/UnionBase.h"
using namespace prototypeInd::templates;
template<class arg,class conv>
struct File{
};
template<class arg,class conv>
struct Directory : public File<arg,conv>{
};
template<class arg,class conv>
struct FS{
typedef Directory<arg,conv> Directory;
typedef File<arg,conv> File;
};
template<class arg,class conv>
struct DFile : public virtual File<arg,conv>{
};
template<class arg,class conv>
struct DDirectory : public virtual Directory<arg,conv>, public virtual DFile<arg,conv>{
void foo(typename conv::Directory::UnionType& d){
}
};
template<class arg,class conv>
struct DFS : public virtual FS<arg,conv>{
typedef DFile<arg,conv> File;
typedef DDirectory<arg,conv> Directory;
};
template<class arg,template<class,class> class fsa,template<class,class> class fsb>
struct FSU{
typedef UnionBase<FS<arg,FSU>,fsa<arg,FSU>,fsb<arg,FSU> > FS;
typedef UnionBase<typename ::FS<arg,FSU>::Directory,typename fsa<arg,FSU>::Directory,typename fsb<arg,FSU>::Directory> Directory;
typedef UnionBase<typename ::FS<arg,FSU>::File,typename fsa<arg,FSU>::File,typename fsb<arg,FSU>::File> File;
};
typedef FSU<int,DFS,DFS> thing;
DDirectory<int,thing> d;
int main(int d,char** thing){
}
The error I get is:
invalid use of incomplete type 'struct DDirectory<int, FSU<int, DFS, DFS> >'
Here is UnionBase.h (its huge but don't worry all of this is working):
#ifndef prototypeInd_templates_UnionBase_h
#define prototypeInd_templates_UnionBase_h
#include <type_traits>
template<class Type, uint64_t time,class First,class... Array>
class IndexOf_{
static const bool isSame = std::is_same<First,Type>::value;
public:
static const uint64_t value = isSame ? time : IndexOf_<Type,time+1,Array...>::value;
};
template<class Type, uint64_t time, class First>
class IndexOf_<Type,time,First>{
public:
//static_assert(std::is_same<First,Type>::value,"Not contained in list");
static const uint64_t value = time;
};
template<class Type,class... Array>
using IndexOf = IndexOf_<Type,0,Array...>;
template<class Target, class First, class... Rest>
class ContainsType{
public:
static const bool value = std::is_same<Target, First>::value ? true : ContainsType<Target,Rest...>::value;
};
template<class Target, class First>
class ContainsType<Target,First>{
public:
static const bool value = std::is_same<Target, First>::value;
};
//Best is the highes so far while rest is the rest of the list
template <class Best,class First, class... Rest>
class GetMaxSize{
//typedef typename GetFirstType<Rest...>::value First;
static const bool FirstBigger = sizeof(First) > sizeof(Best);
public:
typedef typename std::conditional<FirstBigger,typename GetMaxSize<First,Rest...>::value,typename GetMaxSize<Best,Rest...>::value >::type value;
};
template<class Best, class First>
class GetMaxSize<Best,First>{
static const bool FirstBigger = sizeof(First) > sizeof(Best);
public:
typedef typename std::conditional<FirstBigger,First,Best >::type value;
};
template<class From,uint16_t index,class UT,class First,class... Array>
struct cast{
static void apply(From** t,UT* f){
if (index == f->GetActive()){
*t = &((First)(*f));
}
else{
cast<From,index+1,UT,Array...>::apply(t,f);
}
}
};
template<class From,uint16_t index,class UT,class First>
struct cast<From,index,UT,First>{
static void apply(From** t,UT* f){
if (index == f->GetActive()){
*t = &((First)(*f));
}
}
};
template<class... Values>
class UnionType{
typedef typename GetMaxSize<Values...>::value internal_t;
internal_t data;
uint16_t active;
public:
template<class CastFrom, class Dummy = typename std::enable_if<ContainsType<CastFrom,Values...>::value, int>::type >
UnionType(CastFrom&& d) : data(reinterpret_cast<internal_t&>(d)),active(IndexOf<CastFrom,Values...>::value){
}
template<class CastTo, class Condition = typename std::enable_if<ContainsType<CastTo,Values...>::value,int>::type >
operator CastTo const&() const{
return reinterpret_cast<const CastTo&>(data);
}
uint16_t GetActive() const{
return active;
}
//This one actually uses casting of the active data type
template<class CastTo, class Condition = typename std::enable_if<!ContainsType<CastTo,Values...>::value,int>::type >
explicit operator CastTo*() const{
CastTo temp;
CastTo* ret = &temp;
cast<CastTo,0,UnionType,Values...>::apply(&ret,this);
return ret;
}
};
namespace prototypeInd{namespace templates{
template<class Base, class Thing>
struct IsPublicBase{
static const bool value = std::is_base_of<Base,Thing>::value && std::is_convertible<Thing*,Base*>::value;
};
template<class Base, class First, class... Rest>
struct AllInheritFrom{
static const bool value = IsPublicBase<Base,First>::value ? AllInheritFrom<Base,Rest...>::value : false;
};
template<class Base, class First>
struct AllInheritFrom<Base,First>{
static const bool value = IsPublicBase<Base,First>::value;
};
template<template<class> class Function,class First,class... Args>
struct AllFullfill{
static const bool value = Function<First>::value ? AllFullfill<Function,Args...>::value : false;
};
template<template<class> class Function,class First>
struct AllFullfill<Function,First>{
static const bool value = Function<First>::value;
};
template<class Base, class... Rest>
class UnionBase{
static_assert(AllInheritFrom<Base,Rest...>::value, "All of the elements of UnionBase must have Base as a public base");
public:
typedef UnionType<Rest...> UnionType;
private:
UnionType internal;
public:
UnionBase() : internal(typename GetFirstType<Rest...>::value()){};
UnionBase(Base&& value) : internal(value){
}
operator UnionType&(){
return internal;
}
Base* operator ->() const{
//return 0;
return &((Base&)internal);
}
};
//template<class Base, class... Rest>
//using UnionBase = UnionBase_<Base,Rest...>*;
}}
#endif
So the real question is: what should I do to make this work? I am open to restructuring a little bit, but after hours of trying everything I can think of I am almost ready to scrap the whole thing and start again.
The problem is that in certain places of code your classes are really incomplete.
According to [class.mem]/1:
A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier.
Within the class member-specification, the class is regarded as complete within function bodies,
default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and
brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise
it is regarded as incomplete within its own class member-specification.
When applied to your code, this means in particular that the class is incomplete within function parameter lists. Now let's look at the definition of DDirectory::foo():
template<class arg,class conv>
struct DDirectory : public virtual Directory<arg,conv>, public virtual DFile<arg,conv>{
void foo(typename conv::Directory::UnionType& d){
}
};
In the instantiation DDirectory<int,thing> conv is FSU<int,DFS,DFS>, so instantiation of it involves instantiation of UnionBases inside, and eventially to this:
static_assert(AllInheritFrom<Base,Rest...>::value, "All of the elements of UnionBase must have Base as a public base");
where one of classes is DDirectory<int,thing>. Remember, all this happens in the deducing the type of the parameter of foo(), so DDirectory<int,thing> is incomplete, and that's what the compiler is saying.
You could try to move that static_assert for example to the constructor of UnionBase, but it doesn't solve other error which I think is impossible to fix, and the reason is the same:
error: invalid application of 'sizeof' to an incomplete type 'DDirectory<int, FSU<int, DFS, DFS> >'
static const bool FirstBigger = sizeof(First) > sizeof(Best);
^~~~~~~~~~~~~
Here is a minimized example reproducing the problem:
#include <type_traits>
template <typename T1, typename T2>
struct BiggerType {
using type = typename std::conditional<(sizeof(T1) > sizeof(T2)), T1, T2>::type;
};
template<typename T>
struct S {
using B = BiggerType<S, int>;
// This causes the instantiation of BiggerType,
// leading to calculation of sizeof(S) which is incomplete
void foo(const typename B::type& bt) {
}
};
int main() {
S<int> s;
}
Or in very compressed form,
template<typename T>
struct S {
// Same problem here
void foo(typename std::conditional<(sizeof(S) > sizeof(int)), S, int>::type&) {
}
};
I have something working but it seems awfully verbose.
#include <array>
#include <iostream>
#include <type_traits>
using DataArrayShort = std::array<unsigned char, 4>;
using DataArrayLong = std::array<unsigned char, 11>;
// Two base classes the later template stuff should choose between
class Short
{
public:
Short(const DataArrayShort & data) { /* do some init */}
};
class Long
{
public:
Long(const DataArrayLong & data) { /* do some init */}
};
// Concrete derived of the two bases
class S1 : public Short
{
public:
using Short::Short;
operator std::string() { return "S1!";}
};
class S2 : public Short
{
public:
using Short::Short;
operator std::string() { return "S2!";}
};
class L1 : public Long
{
public:
using Long::Long;
operator std::string() { return "L1!";}
};
class L2 : public Long
{
public:
using Long::Long;
operator std::string() { return "L2!";}
};
// Variables that will be modified by parsing other things before calling parse<>()
bool shortDataSet = false;
bool longDataSet = false;
DataArrayShort shortData;
DataArrayLong longData;
// Begin overly verbose template stuff
template<bool IsShort, bool IsLong>
bool getFlag();
template<>
bool getFlag<true, false>()
{
return shortDataSet;
}
template<>
bool getFlag<false, true>()
{
return longDataSet;
}
template<bool IsShort, bool IsLong>
struct RetType
{};
template<>
struct RetType<true, false>
{
typedef DataArrayShort & type;
};
template<>
struct RetType<false, true>
{
typedef DataArrayLong & type;
};
template<bool IsShort, bool IsLong>
typename RetType<IsShort, IsLong>::type getData();
template<>
DataArrayShort & getData<true, false>()
{
return shortData;
}
template<>
DataArrayLong & getData<false, true>()
{
return longData;
}
template<typename T>
inline std::string parse()
{
// First test if I can create the type with initialized data
if (getFlag<std::is_base_of<Short, T>::value, std::is_base_of<Long, T>::value>())
{
// If it's initialized, Then create it with the correct array
T t(getData<std::is_base_of<Short, T>::value, std::is_base_of<Long, T>::value>());
return t;
}
else
{
return "with uninitialized data";
}
}
// End overly verbose template stuff
int main(int argc, const char * argv[])
{
// Something things that may or may not set shortDataSet and longDataSet and give shortData and longData values
std::cout << parse<S1>() << std::endl;
shortDataSet = true;
std::cout << parse<S1>() << std::endl;
std::cout << parse<L2>() << std::endl;
longDataSet = true;
std::cout << parse<L2>() << std::endl;
}
The syntax that's important to me is parse(). Within parse, I want to make sure I route to the correct flag and data to instantiate ConcreteType with.
I'm starting to think I can't use a function template to do what I want - I'm better off using a class template with static function members.
Using std::is_base_of seems clumsy - can I use built-in inheritance with overloads rather than is_base_of with overloads based on Short and Long?
RetType seems unnecessary but there seemed to be no other way to declare getData().
Part of the difficulty is that I need to determine the data to initialize t with before instantiating it.
I don't like the separate template bools for IsShort and IsLong - it won't scale.
What can I do to tighten this up?
You should just forward to a dispatcher that is SFINAE-enabled. Start with an inheritance tree:
template <int I> struct chooser : chooser<I-1> { };
template <> struct chooser<0> { };
Forward to it:
template <typename T>
std::string parse() { return parse_impl<T>(chooser<2>{}); }
And write your cases:
template <typename T,
typename = std::enable_if_t<std::is_base_of<Short, T>::value>
>
std::string parse_impl(chooser<2> ) { // (1)
// we're a Short!
if (shortDataSet) {
return T{shortData};
}
else {
return "with uninitialized data";
}
}
template <typename T,
typename = std::enable_if_t<std::is_base_of<Long, T>::value>
>
std::string parse_impl(chooser<1> ) { // (2)
// we're a Long!
if (longDataSet) {
return T{longData};
}
else {
return "with uninitialized data";
}
}
template <typename >
std::string parse_impl(chooser<0> ) { // (3)
// base case
return "with uninitialized data";
}
If T inherits from Short, (1) is called. Else, if it inherits from Long, (2) is called. Else, (3) is called. This is a handy way to do SFINAE on multiple potentially-overlapping criteria (since you can, after all, inherit from both Short and Long right?)
A little bit of refactoring goes a long way:
template<class T, bool IsShort = std::is_base_of<Short, T>::value,
bool IsLong = std::is_base_of<Long, T>::value>
struct data_traits { };
template<class T>
struct data_traits<T, true, false> {
static bool getFlag() { return shortDataSet; }
static DataArrayShort & getData() { return shortData; }
};
template<class T>
struct data_traits<T, false, true> {
static bool getFlag() { return longDataSet; }
static DataArrayLong & getData() { return longData; }
};
template<typename T>
inline std::string parse()
{
using traits = data_traits<T>;
// First test if I can create the type with initialized data
if (traits::getFlag())
{
// If it's initialized, Then create it with the correct array
T t(traits::getData());
return t;
}
else
{
return "with uninitialized data";
}
}
I can suggest to use traits technique, like other answer. But my solution is better in the way that it allows scability of this solution, I mean no more true, false, ... flags in your code;)
So starting from this comment:
// Variables that will be modified by parsing other things before calling parse<>()
Change your code to more scalable version.
First connect base types with data types:
template <typename BaseType>
class BaseDataTypeTraits;
template <> struct BaseDataTypeTraits<Short>
{
typedef DataArrayShort DataType;
};
template <> struct BaseDataTypeTraits<Long>
{
typedef DataArrayLong DataType;
};
Then define your base type traits:
template <typename BaseType>
struct BaseParseTypeTraits
{
static bool dataSet;
typedef typename BaseDataTypeTraits<BaseType>::DataType DataType;
static DataType data;
};
template <typename BaseType>
bool BaseParseTypeTraits<BaseType>::dataSet = false;
template <typename BaseType>
typename BaseParseTypeTraits<BaseType>::DataType BaseParseTypeTraits<BaseType>::data;
And parse traits for each specific base type:
template <typename T, typename EnableIf = void>
class ParseTypeTraits;
template <typename T>
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<Short, T>::value>::type>
: public BaseParseTypeTraits<Short>
{};
template <typename T>
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<Long, T>::value>::type>
: public BaseParseTypeTraits<Long>
{};
And your parse is then almost identical to other "traits" answer:
template<typename T>
inline std::string parse()
{
typedef ParseTypeTraits<T> TTraits;
// First test if I can create the type with initialized data
if (TTraits::dataSet)
{
// If it's initialized, Then create it with the correct array
T t(TTraits::data);
return t;
}
else
{
return "with uninitialized data";
}
}
int main(int argc, const char * argv[])
{
// Something things that may or may not set shortDataSet and longDataSet and give shortData and longData values
std::cout << parse<S1>() << std::endl;
BaseParseTypeTraits<Short>::dataSet = true;
std::cout << parse<S1>() << std::endl;
std::cout << parse<L2>() << std::endl;
BaseParseTypeTraits<Long>::dataSet = true;
std::cout << parse<L2>() << std::endl;
}
Working example: ideone
[UPDATE]
In this example code I also added what is required to add new base and data type.
I mean you have this:
using DataArrayNew = std::array<unsigned char, 200>;
class New
{
public:
New(const DataArrayNew & data) { /* do some init */}
};
class N1 : public New
{
public:
using New::New;
operator std::string() { return "N1!";}
};
And to make these types be supported by your parse - you need only these two specialization:
template <> struct BaseDataTypeTraits<New>
{
typedef DataArrayNew DataType;
};
template <typename T>
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<New, T>::value>::type>
: public BaseParseTypeTraits<New>
{};
This can be enclosed in a macro:
#define DEFINE_PARSE_TRAITS_TYPE(BaseTypeParam, DataTypeParam) \
template <> struct BaseDataTypeTraits<BaseTypeParam> \
{ \
typedef DataTypeParam DataType; \
}; \
template <typename T> \
class ParseTypeTraits<T, \
typename std::enable_if< \
std::is_base_of<BaseTypeParam, T>::value>::type> \
: public BaseParseTypeTraits<BaseTypeParam> \
{}
So support for new types is as simple as this:
DEFINE_PARSE_TRAITS_TYPE(New, DataArrayNew);
The more simplification can be achieved when we can require that base type has its datatype defined within its class definition - like here:
class New
{
public:
typedef DataArrayNew DataType;
New(const DataArrayNew & data) { /* do some init */}
};
Then we can have generic BaseDataTypeTraits definition:
template <typename BaseType>
struct BaseDataTypeTraits
{
typedef typename BaseType::DataType DataType;
};
So for new type - you only require to add specialization for DataTypeTraits:
template <typename T>
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<New, T>::value>::type>
: public BaseParseTypeTraits<New>
{};
I want to get pointer to base class from boost variant, if I put orignally pointer to derived class. Is there some way to achive this . The following code does not work.
class A{ public: virtual ~A(){}}; class B : public A{};
typedef boost::variant<A*,B*> MyVar;
MyVar var = new B;
A* a = boost::get<A*> (var); // the following line throws exception
Maybe someone have idea how to write my own get function which will test if the requested type is base class of the stored type of in the variant,and then do the appropriate cast
You can write your own visitor with templated operator() like below:
LIVE DEMO
#include <iostream>
#include <boost/variant.hpp>
#include <type_traits>
struct A { virtual ~A() {} virtual void foo() {} };
struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };
template <typename T>
struct visitor : boost::static_visitor<T>
{
private:
using Base = typename std::remove_pointer<
typename std::remove_cv<
typename std::remove_reference<T>::type
>::type
>::type;
template <typename U>
T get(U& u, std::true_type) const
{
return u;
}
template <typename U>
T get(U& u, std::false_type) const
{
throw boost::bad_get{};
}
public:
template <typename U>
T operator()(U& u) const
{
using Derived = typename std::remove_pointer<
typename std::remove_cv<
typename std::remove_reference<U>::type
>::type
>::type;
using tag = std::integral_constant<bool
, (std::is_base_of<Base, Derived>::value
|| std::is_same<Base, Derived>::value)
&& std::is_convertible<U, T>::value>;
return get(u, tag{});
}
};
template <typename T, typename... Args>
T my_get(boost::variant<Args...>& var)
{
return boost::apply_visitor(visitor<T>{}, var);
}
int main()
{
boost::variant<A*,B*> var = new B;
A* a = my_get<A*>(var); // works!
a->foo();
B* b = my_get<B*>(var); // works!
b->foo();
}
Output:
B::foo()
B::foo()
Q & A section:
This solution is weird!
No, it is not. This is exactly what the visitor classes in Boost.Variant are for. Similar solution already exists in latest release of Boost.Variant, which is boost::polymorphic_get<T>. Sadly it was designed for other purposes and cannot be used here.
Hi thank you all for your answers and comments
I came to the following which decides at compile time if types are inherited from each other. And it seems to work, and it seems much easier to me to understand.
#include <iostream>
#include <boost/variant.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility.hpp>
using namespace boost::type_traits;
struct A { virtual ~A() {} virtual void foo() {} };
struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };
typedef boost::variant<B*,A*,C*> MyVar;
template <typename A,typename B>
struct types_are_inheritance_related
{
static const bool value=
ice_or<
boost::is_base_of<A, B>::value,
boost::is_base_of<B, A>::value
>::value;
};
template<class Base>
class get_visitor
: public boost::static_visitor<Base*> { public:
template<class T>
Base* operator()( T* t, typename boost::enable_if<types_are_inheritance_related<Base,T> >::type* dummy = 0)
{
Base* b = dynamic_cast<Base*> ( t);
return b;
}
template<class T>
Base* operator()( T* t, typename boost::disable_if<types_are_inheritance_related<Base,T> >::type* dummy = 0)
{
return 0;
}
};
template<class T>
T* get_var_value(MyVar& var)
{
get_visitor<T> visitor;
T* aa= var.apply_visitor(visitor);
return aa;
}
int main()
{
MyVar var = new B;
A* a = get_var_value<A*>(var); // works!
a->foo();
B* b = get_var_value<B*>(var); // works!
b->foo();
}