I have the following code:
#include <string>
enum class Hobbit {
// typedef HobbitHelper helper;
UNKNOWN = -1, Bilbo, Frodo, Samwise
};
struct HobbitHelper {
static Hobbit decode(std::string const& s) {
if (s == "Bilbo") {
return Hobbit::Bilbo;
}
else if (s == "Frodo") {
return Hobbit::Frodo;
}
else if (s == "Samwise") {
return Hobbit::Samwise;
}
else {
return Hobbit::UNKNOWN;
}
}
};
enum class Wizard {
// typedef Apprentice helper;
UNKNOWN = -1, Gandalf, Radagast, Saruman
};
struct Apprentice { // WizardHelper :)
static Wizard decode(std::string const& s) {
if (s == "Gandalf") {
return Wizard::Gandalf;
}
else if (s == "Radagast") {
return Wizard::Radagast;
}
else if (s == "Saruman") {
return Wizard::Saruman;
}
else {
return Wizard::UNKNOWN;
}
}
};
template <typename T>
T
decoder(std::string s)
{
return ??::decode(s);
// if the typedefs were allowed, I could use T::helper::decode()
}
int main()
{
std::string s{ "Rincewind" };
auto h = decoder<Hobbit>(s);
auto w = decoder<Wizard>(s);
}
How can I arrange to call the appropriate helper class (HobbitHelper or Apprentice) in decoder? I can't declare a nested type inside the enum, as if it was a class. I also tried deriving the helper from the enum (since the helper itself has no data), but that isn't allowed either.
Any ideas?
You can just have the helper type trait be external and templated on the enum type, with an explicit specialization for each enum:
template <typename T> struct type_is { using type = T; };
template <typename > struct helper;
template <> struct helper<Hobbit> : type_is<HobbitHelper> { };
template <> struct helper<Wizard> : type_is<Apprentice> { };
template <typename T>
using helper_t = typename helper<T>::type;
And then decode would just access that:
template <typename T>
T decoder(std::string s)
{
return helper_t<T>::decode(s);
}
The simplest way to do it is to use ADL. You can use a type tag to make the compiler look in the appropriate namespace.
Consider:
template<typename T> struct adl_tag {};
namespace MiddleEarth {
enum class Hobbit {
// typedef HobbitHelper helper;
UNKNOWN = -1, Bilbo, Frodo, Samwise
};
Hobbit decode(std::string const& s, adl_tag<Hobbit>) {
if (s == "Bilbo") {
return Hobbit::Bilbo;
}
else if (s == "Frodo") {
return Hobbit::Frodo;
}
else if (s == "Samwise") {
return Hobbit::Samwise;
}
else {
return Hobbit::UNKNOWN;
}
}
}
template<typename T> T decode(std::string s) {
return decode(s, adl_tag<T>());
}
This is the solution employed by pretty much all C++ libraries- more or less. There's basically no additional effort involved. I didn't even have to mention Wizard.
My suggestion would be partial template specialization, although the answer from #Barry might be more like what you are looking for.
template <typename T>
T decoder(std::string s);
template<>
Hobbit decoder(std::string s)
{
return HobbitHelper::decode(s);
}
template<>
Wizard decoder(std::string s)
{
return Apprentice::decode(s);
}
Aside helper problem there is better solution than cascade if:
static Hobbit decode(std::string const& s) {
static std::unordered_map<std::strinng,Hobbit> choice {
{ "Bilbo", Hobbit::Bilbo },
{ "Frodo", Hobbit::Frodo },
{ "Samwise", Hobbit::Samwise }
};
auto f = choice.find( s );
return f != choice.end() ? f->second : Hobbit::UNKNOWN;
}
In the end I went with a slightly modified version of Barry's answer
template <typename T>
struct enumclass {
};
template<>
struct enumclass<Hobbit> {
using helper = HobbitHelper;
};
template<>
struct enumclass<Wizard> {
using helper = Apprentice;
};
because it lets me write the more mnemonic
template <typename T>
T
decoder(std::string s)
{
return enumclass<T>::helper::decode(s);
}
All the specializations can be distributed (i.e. enumclass <Hobbit> is in hobbit.h; enumclass<Wizard> is in wizard.h). All these headers must include a small header with the unspecialized template.
Related
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;
};
I am writing an Abstract Factory using C++ templates and was hit by a small obstacle. Namely, a generic class T may provide one or more of the following ways to construct objects:
static T* T::create(int arg);
T(int arg);
T();
I am writing the abstract factory class so that it can automatically try these three potential constructions in the given order:
template <class T>
class Factory {
public:
T* create(int arg) {
return T::create(arg); // first preference
return new T(arg); // this if above does not exist
return new T; // this if above does not exist
// compiler error if none of the three is provided by class T
}
};
How do I achieve this with C++ template? Thank you.
Something along this line should work:
struct S { static auto create(int) { return new S; } };
struct T { T(int) {} };
struct U {};
template<int N> struct tag: tag<N-1> {};
template<> struct tag<0> {};
class Factory {
template<typename C>
auto create(tag<2>, int N) -> decltype(C::create(N)) {
return C::create(N);
}
template<typename C>
auto create(tag<1>, int N) -> decltype(new C{N}) {
return new C{N};
}
template<typename C>
auto create(tag<0>, ...) {
return new C{};
}
public:
template<typename C>
auto create(int N) {
return create<C>(tag<2>{}, N);
}
};
int main() {
Factory factory;
factory.create<S>(0);
factory.create<T>(0);
factory.create<U>(0);
}
It's based on sfinae and tag dispatching techniques.
The basic idea is that you forward the create function of your factory to a set of internal functions. These functions test the features you are looking for in order because of the presence of tag and are discarded if the test fail. Because of sfinae, as long as one of them succeeds, the code compiles and everything works as expected.
Here is a similar solution in C++17:
#include <type_traits>
#include <iostream>
#include <utility>
struct S { static auto create(int) { return new S; } };
struct T { T(int) {} };
struct U {};
template<typename C> constexpr auto has_create(int) -> decltype(C::create(std::declval<int>()), bool{}) { return true; }
template<typename C> constexpr auto has_create(char) { return false; }
struct Factory {
template<typename C>
auto create(int N) {
if constexpr(has_create<C>(0)) {
std::cout << "has create" << std::endl;
return C::create(N);
} else if constexpr(std::is_constructible_v<C, int>) {
std::cout << "has proper constructor" << std::endl;
return new C{N};
} else {
std::cout << "well, do it and shut up" << std::endl;
(void)N;
return C{};
}
}
};
int main() {
Factory factory;
factory.create<S>(0);
factory.create<T>(0);
factory.create<U>(0);
}
Thanks to #StoryTeller and #Jarod42 for the help in this difficult morning.
See it up and running on wandbox.
Okay, thanks to the answer by #skypjack I was able to come up with a more compatible solution that works with pre c++11 compilers. The core idea is the same, i.e. using tag dispatching for ordered testing. Instead of relying on decltype, I used sizeof and a dummy class for SFINAE.
struct S { static auto create(int) { return new S; } };
struct T { T(int) {} };
struct U {};
template<class C, int=sizeof(C::create(0))> struct test_1 { typedef int type; };
template<class C, int=sizeof(C(0))> struct test_2 { typedef int type; };
template<class C, int=sizeof(C())> struct test_3 { typedef int type; };
template<int N> struct priority: priority<N-1> {};
template<> struct priority<0> {};
class Factory {
template<typename C>
C* create(priority<2>, typename test_1<C>::type N) {
return C::create(N);
}
template<typename C>
C* create(priority<1>, typename test_2<C>::type N) {
return new C(N);
}
template<typename C>
C* create(priority<0>, typename test_3<C>::type N) {
return new C();
}
public:
template<typename C>
C* create(int N) {
return create<C>(priority<2>(), N);
}
};
int main() {
Factory factory;
factory.create<S>(0);
factory.create<T>(0);
factory.create<U>(0);
}
Not sure if it is even possible to stuff the sizeof part into the private function signatures; if so, we can get rid of the dummy classes as well.(failed) The slightly ugly part is to use constants (0 in this case) for sizeof operator, which may get tricky if the constructors take arguments of very complicated types.
My real example is quite big, so I will use a simplified one. Suppose I have a data-type for a rectangle:
struct Rectangle {
int width;
int height;
int computeArea() {
return width * height;
}
}
And another type that consumes that type, for example:
struct TwoRectangles {
Rectangle a;
Rectangle b;
int computeArea() {
// Ignore case where they overlap for the sake of argument!
return a.computeArea() + b.computeArea();
}
};
Now, I don't want to put ownership constraints on users of TwoRectangles, so I would like to make it a template:
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
// Ignore case where they overlap for the sake of argument!
return a.computeArea() + b.computeArea();
}
};
Usages:
TwoRectangles<Rectangle> x;
TwoRectangles<Rectangle*> y;
TwoRectangles<std::shared_ptr<Rectangle>> z;
// etc...
The problem is that if the caller wants to use pointers, the body of the function should be different:
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
assert(a && b);
return a->computeArea() + b->computeArea();
}
};
What is the best way of unifying my templated function so that the maxiumum amount of code is reused for pointers, values and smart pointers?
One way of doing this, encapsulating everything within TwoRectangles, would be something like:
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
return areaOf(a) + areaOf(b);
}
private:
template <class U>
auto areaOf(U& v) -> decltype(v->computeArea()) {
return v->computeArea();
}
template <class U>
auto areaOf(U& v) -> decltype(v.computeArea()) {
return v.computeArea();
}
};
It's unlikely you'll have a type for which both of those expressions are valid. But you can always add additional disambiguation with a second argument to areaOf().
Another way, would be to take advantage of the fact that there already is a way in the standard library of invoking a function on whatever: std::invoke(). You just need to know the underlying type:
template <class T, class = void>
struct element_type {
using type = T;
};
template <class T>
struct element_type<T, void_t<typename std::pointer_traits<T>::element_type>> {
using type = typename std::pointer_traits<T>::element_type;
};
template <class T>
using element_type_t = typename element_type<T>::type;
and
template<typename T>
struct TwoRectangles {
T a;
T b;
int computeArea() {
using U = element_type_t<T>;
return std::invoke(&U::computeArea, a) +
std::invoke(&U::computeArea, b);
}
};
I actually had a similar problem some time ago, eventually i opted not to do it for now (because it's a big change), but it spawned a solution that seems to be correct.
I thought about making a helper function to access underlying value if there is any indirection. In code it would look like this, also with an example similar to yours.
#include <iostream>
#include <string>
#include <memory>
namespace detail
{
//for some reason the call for int* is ambiguous in newer standard (C++14?) when the function takes no parameters. That's a dirty workaround but it works...
template <class T, class SFINAE = decltype(*std::declval<T>())>
constexpr bool is_indirection(bool)
{
return true;
}
template <class T>
constexpr bool is_indirection(...)
{
return false;
}
}
template <class T>
constexpr bool is_indirection()
{
return detail::is_indirection<T>(true);
}
template <class T, bool ind = is_indirection<T>()>
struct underlying_type
{
using type = T;
};
template <class T>
struct underlying_type<T, true>
{
using type = typename std::remove_reference<decltype(*(std::declval<T>()))>::type;
};
template <class T>
typename std::enable_if<is_indirection<T>(), typename std::add_lvalue_reference<typename underlying_type<T>::type>::type>::type underlying_value(T&& val)
{
return *std::forward<T>(val);
}
template <class T>
typename std::enable_if<!is_indirection<T>(), T&>::type underlying_value(T& val)
{
return val;
}
template <class T>
typename std::enable_if<!is_indirection<T>(), const T&>::type underlying_value(const T& val)
{
return val;
}
template <class T>
class Storage
{
public:
T val;
void print()
{
std::cout << underlying_value(val) << '\n';
}
};
template <class T>
class StringStorage
{
public:
T str;
void printSize()
{
std::cout << underlying_value(str).size() << '\n';
}
};
int main()
{
int* a = new int(213);
std::string str = "some string";
std::shared_ptr<std::string> strPtr = std::make_shared<std::string>(str);
Storage<int> sVal{ 1 };
Storage<int*> sPtr{ a };
Storage<std::string> sStrVal{ str };
Storage<std::shared_ptr<std::string>> sStrPtr{ strPtr };
StringStorage<std::string> ssStrVal{ str };
StringStorage<const std::shared_ptr<std::string>> ssStrPtr{ strPtr };
sVal.print();
sPtr.print();
sStrVal.print();
sStrPtr.print();
ssStrVal.printSize();
ssStrPtr.printSize();
std::cout << is_indirection<int*>() << '\n';
std::cout << is_indirection<int>() << '\n';
std::cout << is_indirection<std::shared_ptr<int>>() << '\n';
std::cout << is_indirection<std::string>() << '\n';
std::cout << is_indirection<std::unique_ptr<std::string>>() << '\n';
}
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 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)
};