I want to implement a class template that:
behaves like a function
it's input and output variables are all shared.
relatively easy to use.
As a result, I construct the following:
// all input/output variable's base class
class basic_logic_parameter;
// input/output variable, has theire value and iterators to functions that reference to this variable
template <typename FuncIterator, typename ValueType>
class logic_parameter
:public basic_logic_parameter
{
private:
std::list<FuncIterator> _refedFuncs;
ValueType _val;
public:
};
// all `function`'s base class
class basic_logic_function
{
public:
virtual ~basic_logic_function() = 0;
};
// the function, has input/output variable
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base
:public basic_logic_function
{
private:
std::shared_ptr<logic_parameter<FuncIterator, R>> _ret;
std::tuple<std::shared_ptr<logic_parameter<FuncIterator, Args>>...> _args;
public:
template <std::size_t N>
decltype(auto) arg()
{
return std::get<N>(_args);
}
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N> type;
};
template <std::size_t N>
using arg_type_t = arg_type<N>::type;
decltype(auto) ret()
{
return _ret;
}
};
I wish to use as these like:
// drawing need color and a pen
struct Color
{
};
struct Pen
{
};
struct Iter
{
};
class Drawer
:public logic_function_base<Iter, void(Color, Pen)>
{
public:
void draw()
{
arg_type_t<0> pColor; // wrong
}
}
My compiler can not pass this code through, why? I just want convert a template parameter pack to std::tuple of std::shared_ptr of them.
for example:
Given struct A, int, struct C, I want to have:
std::tuple<
std::shared_ptr<logic_parameter<A>>,
std::shared_ptr<logic_parameter<int>>,
std::shared_ptr<logic_parameter<C>>,
>
The problem (once the small errors are fixed1) is that you instantiate:
logic_function_base<Iter, void(Color, Pen)>
...meaning that FuncIterator is Iter and R is void(Color, Pen), so Args is emtpy <>, so decltype(_args) is an empty std::tuple<>, and your code fails to obtain the type of the 0th element of an empty tuple, which is legit.
What you want is partial specialization of logic_function_base:
template <typename F, typename T>
class logic_function_base;
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base<FuncIterator, R(Args...)>: public basic_logic_function {
};
1 Small mistakes in your current code:
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N, decltype(_args)> type; // Missing the tuple type
};
template <std::size_t N>
using arg_type_t = typename arg_type<N>::type; // Missing a typename
This may not answer your whole question, but you could use the following trait to wrap tuple element types.
template <typename T> struct wrap;
template <typename... T>
struct wrap<std::tuple<T...>> {
using type = std::tuple<std::shared_ptr<logic_parameter<T>>...>;
}
template <typename T>
using wrap_t = typename wrap<T>::type;
You can then use it like this:
std::tuple<int,double,char> t1;
wrap_t<decltype(t)> t2;
The type of t2 is std::tuple<std::shared_ptr<logic_parameter<int>>,std::shared_ptr<logic_parameter<double>>,std::shared_ptr<logic_parameter<char>>>.
Related
I am trying to find way to design a class template so that an int value is passed, and several function signatures as well as argument lists are dependent on this value.
Particularly, considering MyClass:
template <int N>
class MyClass {
typedef SomeType<int, int, int, /* ... N times*/ > MyDepType;
myFunction(std::string arg0, std::string arg1, /* ...*/ std::string argN) { /* do stuff */};
public:
MyClass() {
someFunction(float arg0, float arg1, /* ...*/ float argN); // <
someOtherFunction(boost::bind(&MyClass::myFunction, this, _1, _2, /*...*/ _N));
};
};
I'd like to be able to express both the private typedef call, the signature of myFunction and the argument list passed to external functions someFunction and someOtherFunction, which I can't edit/rewrite.
Is there a way to achieve this, using the C++11 standard?
You could use a trick like in this post (Produce std::tuple of same type in compile time given its length by a template argument) to produce a tuple of N elements.
template <int N, typename T>
struct tuple_n_impl {
template <typename... Ts>
using type = typename tuple_n_impl<N - 1, T>::template type<T, Ts...>;
};
template <typename T>
struct tuple_n_impl<0, T> {
template <typename... Ts>
using type = std::tuple<Ts...>;
};
template <int N, typename T>
using tuple_n = typename tuple_n_impl<N, T>::template type<>;
template <int N>
class MyClass {
void int_function(tuple_n<N, int>&& ints);
void float_function(tuple_n<N, float>&& floats);
template <typename T>
void any_function(tuple_n<N, T>&& elements);
};
You might use std::index_sequence (C++14, but implementation in C++11 can be found on SO):
template <std::size_t, typename T>
using always_t = T;
template <std::size_t ... Is>
class MyClassImpl {
using MyDepType = SomeType<always_t<Is, int>...>;
void myFunction(always_t<Is, std::string>... args) { /* do stuff */}
public:
MyClass() {
void someFunction(always_t<Is, std::float>... args);
someOtherFunction([this](always_t<Is, const std::string&>... args) {
return myFunction(args...);
});
//
std::array<std::string, sizeof...(Is)> strings = {std::to_string(Is)...};
myFunction(strings[Is]...);
};
};
template <std::size_t N>
using MyClass = MyClassImpl<std::make_index_sequence<N>>;
I have a function:
template <class T, class Array>
void DumpArrayDebug(Array& out, const T& source)
It is supposed to be used to dump data from array classes in Maya (Autodesk app) into regular vector.
Example of such array types:
MFloatArray;
MColorArray;
MIntArray;
These classes have the same interface, yet they have no base class.
Currently I use this function in a following way:
MFloatArray someInternalMayaFloatData;
...
std::vector<float> buffer;
DumpArrayDebug(buffer, someInternalMayaFloatData);
Looking at this code makes me wonder is it possible to somehow tie 2 classes inside the template through something like lookup table?
So that result should look something like this:
template <class T>
void dumpArrayDbg(const T& source, ClassLookupTable<T>& out)
Thus far I was able to come up with the following monstrosity:
template <typename T>
struct ClassLookupTable
{
T classname;
};
template <>
struct ClassLookupTable<MIntArray>
{
std::vector<int> classname;
};
template <>
struct ClassLookupTable<MFloatArray>
{
std::vector<float> classname;
};
template <>
struct ClassLookupTable<MColorArray>
{
std::vector<MColor> classname;
};
template <class T>
void dumpArrayDbg(const T& source, decltype(ClassLookupTable<T>::classname)& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
Is there a more elegant solution?
This is a standard template metaprogramming technique: a traits type. The only things I would change are using standard template metaprogramming idioms (type is the standard name for a type trait, not classname) and avoid having the trait specify vector:
template <typename T>
struct MayaArrayBaseElementTrait; //The primary template should never be used.
template <>
struct MayaArrayBaseElementTrait<MIntArray>
{
using type = int;
};
template <>
struct MayaArrayBaseElementTrait<MFloatArray>
{
using type = float;
};
template <>
struct MayaArrayBaseElementTrait<MColorArray>
{
using type = MColor;
};
template<typename T>
using MayaArrayBaseElementTrait_t = typename MayaArrayBaseElementTrait<T>::type;
template <class T>
void dumpArrayDbg(const T& source, std::vector<MayaArrayBaseElementTrait_t<T>>& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
This way, the mapping is from the Maya array type to the base element type. This gives you the freedom to create mappings to other types besides vector. You could create a std::array or std::list or whatever you like. Also, if you ever want to change the allocator type for the vector, you are free to do so, unlike your original code.
Is there a more elegant solution?
I propose the following...
Given a simple template type list container
template <typename ...>
struct typeList
{ };
and a recursive template as follows
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
with an helper using to simplify the extraction of type
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
you can create the mapping as follows
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
and get the required type using lookForType or lookForType_t
using l1 = lookForType_t<MIntArray, myList>;
The following is a full compiling example
#include <vector>
#include <type_traits>
template <typename ...>
struct typeList
{ };
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
struct Color {};
struct MFloatArray {};
struct MColorArray {};
struct MIntArray {};
int main()
{
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
using l1 = lookForType_t<MIntArray, myList>;
using l2 = lookForType_t<MFloatArray, myList>;
using l3 = lookForType_t<MColorArray, myList>;
static_assert( std::is_same_v<std::vector<int>, l1>, "!" );
static_assert( std::is_same_v<std::vector<float>, l2>, "!" );
static_assert( std::is_same_v<std::vector<Color>, l3>, "!" );
}
Something is not working quite well for me. Is this the way to declare a class, that accepts only floating point template parameter?
template <typename T, swift::enable_if<std::is_floating_point<T>::value> = nullptr>
class my_float;
I fail to define methods outside this class. Doesn't compile, not sure why
Well... not exactly SFINAE... but maybe, using template specialization? Something as follows ?
template <typename T, bool = std::is_floating_point<T>::value>
class my_float;
template <typename T>
class my_float<T, true>
{
// ...
};
If you really want use SFINAE, you can write
template <typename T,
typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
class my_float
{
// ...
};
or also (observe the pointer there isn't in your example)
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
class my_float // ------------------------------------------------^
{
};
-- EDIT --
As suggested by Yakk (thanks!), you can mix SFINAE and template specialization to develop different version of your class for different groups of types.
By example, the following my_class
template <typename T, typename = void>
class my_class;
template <typename T>
class my_class<T,
typename std::enable_if<std::is_floating_point<T>::value>::type>
{
// ...
};
template <typename T>
class my_class<T,
typename std::enable_if<std::is_integral<T>::value>::type>
{
// ...
};
is developed for in two versions (two different partial specializations), the first one for floating point types, the second one for integral types. And can be easily extended.
You can also use static_assert to poison invalid types.
template <typename T>
class my_float {
static_assert(std::is_floating_point<T>::value,
"T is not a floating point type");
// . . .
};
It's a little bit more direct, in my opinion.
With either of the other approaches, e.g.
template <typename T, bool = std::is_floating_point<T>::value>
class my_float;
template <typename T> class my_float<T, true> { /* . . . */ };
my_float<int,true> is a valid type. I'm not saying that that's a bad approach, but if you want to avoid this, you'll have to encapsulate
my_float<typename,bool> within another template, to avoid exposing the bool template parameter.
indeed, something like this worked for me (thanks to SU3's answer).
template<typename T, bool B = false>
struct enable_if {};
template<typename T>
struct enable_if<T, true> {
static const bool value = true;
};
template<typename T, bool b = enable_if<T,is_allowed<T>::value>::value >
class Timer{ void start(); };
template<typename T, bool b>
void Timer<T,b>::start()
{ \* *** \*}
I am posting this answer because I did not want to use partial specialization, but only define the behavior of the class outside.
a complete workable example:
typedef std::integral_constant<bool, true> true_type;
typedef std::integral_constant<bool, false> false_type;
struct Time_unit {
};
struct time_unit_seconds : public Time_unit {
using type = std::chrono::seconds;
};
struct time_unit_micro : public Time_unit {
using type = std::chrono::microseconds;
};
template<typename T, bool B = false>
struct enable_if {
};
template<typename T>
struct enable_if<T, true> {
const static bool value = true;
};
template<typename T,
bool b = enable_if<T,
std::is_base_of<Time_unit,
T>::value
>::value>
struct Timer {
int start();
};
template<typename T, bool b>
int Timer<T, b>::start() { return 1; }
int main() {
Timer<time_unit_seconds> t;
Timer<time_unit_micro> t2;
// Timer<double> t3; does not work !
return 0;
}
Is anyone knows how to declare generalized template form for the next template specialization:
template <template <class> class Container,
class Value,
class Return,
Return (Container<Value>::*Apply)(const Value &)>
class Example<Container<Value>, Apply>
{
};
Apply must be a pointer to a member function whoes signature is unknown in template declaration.
Do you mean something like this?
template<typename T, typename F>
struct S;
template<template <typename...> class C, typename R, typename... A>
struct S<C<A...>, R(A...)> {
using Apply = R(C<A...>::*)(A...);
// ...
};
As an example:
template<typename... U>
struct T {
int f(int, char) { return 42; }
};
template<typename T, typename F>
struct S;
template<template <typename...> class C, typename R, typename... A>
struct S<C<A...>, R(A...)> {
using Apply = R(C<A...>::*)(A...);
// ...
};
int main() {
S<T<int, char>, int(int, char)>::Apply apply = &T<int, char>::f;
}
Pretty ugly, indeed, but that's what the OP (maybe) asked.
I have a fairly simple variant class which supports a predefined set of types, and provides an enumerate to indicate which of the available types is currently active. Something like this:
class variant
{
enum class type { integer, real, string, etc };
type active_type() const;
/* ... */
};
I would like to make the class into a template where the supported types are supplied as template parameters:
template <typename... T>
class variant
{
const std::type_info& active_type() const; // logical, but can't switch on it
/* ... */
};
A key feature that I rely on for catching errors is that I can switch on the active type and the compiler will warn if any of the possible cases have been missed. This is not possible using the above design (nor using boost::variant).
My question is, is there any way for me to automatically generate an enum with the same number of enumerates as the number of arguments in the parameter pack?
The actual names/values of the enumerates would not matter, as they can be hidden behind constexpr functions used to map the type to the correct enumerate. I could imagine an eventual usage like this:
template <typename... T>
class variant
{
enum class type { T... }; // magic here
// specializations provided to map T into type (for use in case labels)
template <typename T>
static constexpr type type_enum();
type active_type() const;
/* ... */
};
typedef variant<int, float, std::string> myvar;
myvar var;
switch (var.active_type())
{
case myvar::type_enum<int>(): // static constexpr function
...
break;
case myvar::type_enum<float>():
...
break;
} // warning: enumeration for string not handled in switch
This question is two years old, but since there are no answers and I found no solutions elsewhere, here's a way how I have solved the problem.
First obvious problem is enum type with matching number of parameters (to let compiler do switch checking). The only way I see is a series of template specializations.
template <typename Tag, size_t size> struct Enum;
template <typename Tag> struct Enum<Tag, 0> {
enum class Type { };
};
template <typename Tag> struct Enum<Tag, 1> {
enum class Type {
VALUE0
};
};
template <typename Tag> struct Enum<Tag, 2> {
enum class Type {
VALUE0, VALUE1
};
};
template <typename Tag> struct Enum<Tag, 3> {
enum class Type {
VALUE0, VALUE1, VALUE2
};
};
template <typename Tag> struct Enum<Tag, 4> {
enum class Type {
VALUE0, VALUE1, VALUE2, VALUE3
};
};
Yes, it requires some manual input, but it's not a big problem, keeping in mind that number of types is usually limited and new specializations can be added as they are required (i.e. the approach is statically safe). The Tag parameter is used to distinguish enums with same number of values.
Secondly we need some recursive template magic to enumerate types:
template <typename EnumType, typename Type, typename... Types> struct TypeInfo;
template <typename EnumType, typename Type, typename... Types> struct TypeInfo<EnumType, Type, Type, Types...> {
static const EnumType value = EnumType::VALUE0;
};
template <typename EnumType, typename Type, typename FirstType, typename... Types> struct TypeInfo<EnumType, Type, FirstType, Types...> {
static const EnumType value = static_cast<EnumType>(static_cast<typename std::underlying_type<EnumType>::type>(TypeInfo<EnumType, Type, Types...>::value) + 1);
};
Finally a class to bring all pieces together:
template <typename Tag, typename... Ts> class TypedEnum {
private:
struct InternalTag;
public:
static const size_t NUM_TYPES = sizeof...(Ts);
typedef typename Enum<InternalTag, NUM_TYPES>::Type Type;
template <typename T> static constexpr decltype(TypeInfo<Type, T, Ts...>::value) getValue() { // SFINAE guard
return TypeInfo<Type, T, Ts...>::value;
}
};
Voila! You can switch! (Tested with clang)
struct Tag0;
struct Tag1;
typedef TypedEnum<Tag0, int, float, char> Enum0;
typedef TypedEnum<Tag0, bool, float, char> Enum1; // Incompatible with Enum0!
Enum0::Type tpy = Enum0::getValue<char>(); // 2
switch(tpy) {
case Enum0::getValue<int>():
break;
case Enum0::getValue<float>():
break;
case Enum0::getValue<char>():
break;
}
Summary of features:
Statically rejecting types that are not in enum
Statically warn about missing switch cases
Incompatible enum types for different tags and/or parameters in pack
Features missing:
Needs some template magic to reject parameter packs with duplicates
Get the index of the type in a variadic template and use that as the enum value. This way, you don't need to copy pasta all the code for different numbers of types. This doesn't handle duplicate types in the list, but the index will be the first occurrence in the variadic list. If there needs to be duplicate detection it can be added using std::is_same and looping over all the types for each type in a similar fashion to IndexOf.
#include <type_traits>
using std::size_t;
template <size_t Idx, class Q, class... Ts>
struct IndexOfImpl;
// base case
template <size_t Idx, class Q>
struct IndexOfImpl<Idx, Q>{};
template <size_t Idx, class Q, class T>
struct IndexOfImpl<Idx, Q, T>
: public std::conditional_t<
std::is_same<Q, T>::value, // Is Query the same as T?
// if so
std::integral_constant<size_t, Idx>, // inheret from Idx
void> // type not found
{
};
template <size_t Idx, class Q, class T, class... Ts>
struct IndexOfImpl<Idx, Q, T, Ts...>
: public std::conditional_t<
std::is_same<Q, T>::value, // Is Query the same as T?
// if so
std::integral_constant<size_t, Idx>, // inheret from Idx
IndexOfImpl<Idx + 1, Q, Ts...>> // else try the trailing types
{
};
// Initial case at Idx 0
template <class Q, class... Ts>
struct IndexOf : public IndexOfImpl<0, Q, Ts...>
{
};
// Base case
template <class Q>
struct IndexOf<Q>
{
};
// Store the type list in Ts...
template <class... Ts>
struct TypeEnum {
// Get index/enum value of type T
template <class T>
static constexpr decltype(IndexOf<T, Ts...>::value) getValue() {
return IndexOf<T, Ts...>::value;
}
};
template <>
struct TypeEnum<> {};
struct Unknown;
int main() {
using Tags = TypeEnum<Unknown, int, float, long long, unsigned int, double>;
size_t tag = Tags::getValue<double>();
switch(tag) {
case Tags::getValue<int>(): return 0;
case Tags::getValue<float>(): return 0;
case Tags::getValue<long long>(): return 0;
case Tags::getValue<unsigned int>(): return 0;
case Tags::getValue<double>(): return 0;
default: return 1;
}
}
On compiler explorer