Change behavior based on template enum parameter - c++

I've got a communication class which can handle 2 different protocols. The protocol is chosen by a enum template variable.
Now 1 of the 2 protocols only supports 2-byte values while the other supports 4-byte values.
Communicating is done via a template member function. How to change the static_assert so it takes 2 or 4 bytes depending on the class specialization (= chosen protocol)?
#include <iostream>
#include <math.h>
enum Protocol { P1, P2 };
template <Protocol P>
class Communicator {
public:
template <typename T>
void communicate(T arg) {
static_assert(sizeof(arg) <= sizeof(float), "argument size must be <= 4 bytes");
float value = (float)arg;
uint8_t length = sizeof(arg); //length in bytes
_communicate(value, length);
}
void _communicate(float f, uint8_t length) {
std::cout << f;
}
};
EDIT: I can just pick 1 answer as correct. And although I learned the most from Roy, I picked MM's answer because it keeps things as simple as possible. (upvoted both though)

There's several ways to approach this... here is one:
template<Protocol P>
size_t value_size();
template<> size_t value_size<P1>() { return 2; }
template<> size_t value_size<P2>() { return 4; }
// ... inside your other function
static_assert(sizeof(arg) <= value_size<P>(),

Here's a different approach
#include <iostream>
#include <math.h>
#include <cstdint>
// enum Protocol { P1, P2 }; // let's use types with traits instead.
struct P1
{
constexpr static const int protocol_id = 1;
//^^ or maybe use an enum
// type, this may need refactoring
// to fit your code and style.
using data_type = uint16_t; //< or whatever your 2-byte datatype is.
// can add more data traits and even static member functions here
// you could also add protocol specific non-static data if you add a
// P1 data member to your Communicator class.
// A struct with traits is a very good entry point for many compile-time
// polymorphism techniques.
};
struct P2
{
constexpr static const int protocol_id = 2;
using data_type = uint32_t; //< or whatever your 4-byte datatype is.
};
template <typename _Proto>
class Communicator {
public:
using data_type = typename _Proto::data_type;
constexpr static const int proto_id = typename _Proto::protocol_id;
public:
void communicate(data_type arg) // we can use the trait from either P1 or P2
{
float value = (float)arg;
uint8_t length = sizeof(data_type); //length in bytes
_communicate(value, length);
}
void _communicate(float f, uint8_t length)
{
std::cout << f;
}
};
Here's code to convert an enum (If that's what you already have to a class.
enum protocol_t { p1, p2 };
template <protocol_t _p> struct Protocol {};
// simply derive the template specialization from the desired type
template <> struct Protocol<p1> : P1 {};
// simply derive the template specialization from the desired type
template <> struct Protocol<p2> : P2 {};
You can also derive from P1, P2 to help organize code.
struct P1
{
// ... + specialized functions:
void _communicate(value_type x) { ... } // that you'll call from Communicator
void _communicate(const value_type* begin, const value_type* end) { ... }
};
struct P2 { /* same thing */ };
template <typename _Proto>
class Communicator : _Proto // < can control visibility here.
{ ... };

Related

Is there a way to define traits and cast operators for redefined, via `typedef`, basic types?

I've written a pair of converter functions for conversions between the floating point representation of angles in radians and the binary angles (aka BAMS):
// Binary angles that maps into 360 degree circle
typedef uint8_t binang8_t;
typedef uint16_t binang16_t;
typedef uint32_t binang32_t;
typedef uint64_t binang64_t;
// Convert from radians to BAMS
template<typename A, typename T> A to_binang(T val);
// Convert from radians or BAMS
template<typename T, typename A> T to_rads(A val);
The main issue is my is_binangletrait I created for use inside the converters does not protect against use of unsigned integers in place of new types. I know, I can define those new types as structures and it will solve this problem , but I have reason not to declare them as class or struct(to avoid redefining arithmetic operators and to stay compatible with old C payload headers). Is there a trick to get it working with types defined using typedef?
Here is my attempt at implementing that trait :
template<typename T> struct is_binangle {
static const bool value = false;
};
template<> struct is_binangle<binang8_t> {
static const bool value = true;
};
template<> struct is_binangle<binang16_t> {
static const bool value = true;
};
...
Ideally I'd prefer to define custom casts for those types, so that one can convert by just:
double rangle = M_PI/6;
binang16_t bang = binang16_t(rangle);
bang += bang;
double another_rangle = double(another_bang);
I have strong suspicion that it is impossible unless those binary angles are classes/structures . Is it right?
You can use enum for just this purpose:
#include <cstdint>
#include <type_traits>
enum binang8_t : uint8_t {};
template <typename T>
struct is_binang : std::false_type {};
template <>
struct is_binang<binang8_t> : std::true_type {};
// static_assert(is_binang<uint8_t>::value); --> This fails
static_assert(is_binang<binang8_t>::value);
extern "C" void some_c_function(uint8_t);
int main() {
binang8_t val = static_cast<binang8_t>(0);
some_c_function(val);
return 0;
}
(Compiler explorer)
This works because unscoped enumerations are of unique types (not the same with their underlying integer type), and they are implicitly convertible to integers (so you can pass them to C function that accepts integers without casting).
It's not perfect, I admit, as you must cast the result back to binang8_t if you do arithmetic operations, and it does not work for floating points.
It is a half answer (a template class replacing those 4 typedef-s) but yet another problem -- the text-book member template function implementation fails)
In hopes that I will be able to convince C coders that an array of C++
struct blah {
uint16_t value;
};
can be treated as an array of the uint16_t values, I did try going the high road of declaring a class:
template<typename UnsignedInt,
std::enable_if_t<std::is_unsigned<UnsignedInt>::value, int> = 0>
class binangle {
UnsignedInt value;
public:
binangle(UnsignedInt v) : value(v) {}
template<typename F>
binangle(F v) { set_from_radians<F>(v); }
template<typename F>
void set_from_radians(F rad);
template<typename F>
void set_from_degrees(F rad);
template<typename F>
F to_radians(int start_deg = 0) const;
template<typename F>
F to_degrees(int start_deg = 0) const;
template<typename F> operator F() const;
explicit operator UnsignedInt() const { return value; }
};
... it was all dandy till I started implementation of those template member functions - when compiler sees
template<typename UnsignedInt>
template<typename F>
void binangle<UnsignedInt>::set_from_radians(F rad) {
...
it throws
...
/home/sipan/src/vgit/signals/tests/../include/binangles_impl.hpp:23:51: error: invalid use of incomplete type ‘class signals::binangle<UnsignedInt>’
23 | void binangle<UnsignedInt>::set_from_radians(F rad) {
|

c++ Default paramaters: is it possible to override a default parameter without overriding earlier default parameters

I have a function:
int function(int a, int b = 1, int c = 2){
return a+b+c;
}
I want to set the value of the "c" variable to 3, but don't want to set the value of "b"
In a language like python I can do this:
function(23,c=3)
However in c++ I cant find a way to do something like that. All examples I could find involved setting the value of "b" before the value of "c", like this:
function(23,1,3);
How can I set the value of a default parameter directly?
This is not possible in C++ (at least not directly). You have the provide all parameters up to the last one you want to provide, and in the order given by the declaration.
You can not do that in C++.
As a workaround you could wrap all parameters as fields with default value in a class (or a struct). You can then have multiple constructors for that class that allow you to set only those fields you are really interested in changing with respect to default.
It is possible in c++... if you're willing to jump through some hoops.
For fun, here is an example of how it might be done:
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
//
// utility to check whether a type is in a list of types
//
template<class T, class...Ts> struct is_in;
template<class T, class U>
struct is_in<T, U>
: std::is_same<T, U>::type {};
template<class T, class U, class...Rest>
struct is_in<T, U, Rest...>
: std::integral_constant<bool, std::is_same<T, U>::value || is_in<T, Rest...>::value>
{};
//
// a wrapper around fundamental types so we can 'name' types
//
template<class Type, class Tag>
struct fundamental {
using value_type = Type;
using tag_type = Tag;
fundamental(Type x) : _x(x) {}
operator const Type&() const { return _x; }
operator Type&() { return _x; }
Type _x;
};
//
// a utility to figure out a fundamental type's value or to take it's default value if it's not present
//
template<class Fundamental, class Tuple, typename = void>
struct value_of_impl
{
static typename Fundamental::value_type apply(const Tuple& t)
{
return Fundamental::tag_type::dflt;
}
};
template<class Fundamental, class...Types>
struct value_of_impl<Fundamental, std::tuple<Types...>, std::enable_if_t<is_in<Fundamental, Types...>::value>>
{
static typename Fundamental::value_type apply(const std::tuple<Types...>& t)
{
return typename Fundamental::value_type(std::get<Fundamental>(t));
}
};
template<class Fundamental, class Tuple>
decltype(auto) value_of(const Tuple& t)
{
return value_of_impl<Fundamental, Tuple>::apply(t);
}
//
// some tag names to differentiate parameter 'name' types
//
struct a_tag { static constexpr int dflt = 0; };
struct b_tag { static constexpr int dflt = 1; };
struct c_tag { static constexpr int dflt = 2; };
//
// define some parameter 'names'
//
using a = fundamental<int, a_tag>;
using b = fundamental<int, b_tag>;
using c = fundamental<int, c_tag>;
//
// the canonical implementation of the function
//
void func(int a, int b, int c)
{
std::cout << a << ", " << b << ", " << c << std::endl;
}
//
// a version that forwards the values of fundamental types in a tuple, or their default values if not present
//
template<class...Fundamentals>
void func(std::tuple<Fundamentals...> t)
{
func(value_of<a>(t),
value_of<b>(t),
value_of<c>(t));
}
//
// a version that converts a variadic argument list of fundamentals into a tuple (that we can search)
//
template<class...Fundamentals>
void func(Fundamentals&&...fs)
{
return func(std::make_tuple(fs...));
}
//
// a test
//
using namespace std;
auto main() -> int
{
func();
func(a(5));
func(c(10), a(5));
func(b(20), c(10), a(5));
return 0;
}
expected output:
0, 1, 2
5, 1, 2
5, 1, 10
5, 20, 10
You can't do that directly, but you can use Named Parameter Idiom (although criticized).
The idea is to create an object encapsulating all parameters, initialize it using method chaining and finally call the function, so the code would look like:
int v = function(params(23).c(3));
Something like this could be done with the named parameter idiom. Here's how it might look in use to have optional parameters (sans the default parameter values):
/*
int function(int a, int b = 1, int c = 2){
return a+b+c;
}
*/
int function( Parameters &p ) {
/* ... */
}
void someOtherFunction() {
function( Parameters().parmW(/*...*/)
/* parmX was omitted here */
.parmY(/*...*/)
.parmZ(/*...*/)
);
Adding default parameters could be done in a few ways. function could be replaced with a class whose purpose is to perform those actions. Parameters could also be written to know which flags were set, then function passes in default values before it begins executing. I'm sure there's plenty of ways to do this, perhaps some a lot better than what I've suggested.

C++ Metaprogramming: Store integer by type

I want to store integers for given types which should be used during compilation and during runtime.
Up to now I have the following:
template<typename T>
struct my_int { enum { result = -1 }; };
And I specialize for each type:
template<> struct my_int<a_type> { enum { result = 5 }; };
And I can check during compile time (of course the check here would be against another compile time constant):
static_assert(my_int<a_type>::result == 5, "Bla");
Problem:
This works well, as long as the specialization is in the same namespace. But that is an inconvenience I want to get rid of. So I want to be able to use it in every namespace:
namespace foo {
template<> struct my_int<a_type> { enum { result = 5 }; };
}
namespace bar {
template<> struct my_int<b_type> { enum { result = 7 }; };
}
Any ideas how I could do this?
C++11 and boost is ok for my situation, if really needed.
Update: Seems I gave to little information. The types are mainly enum classes. If you're really interested you can see the real implementation here, http://www.codeduce.com/extra/enum_tools, download the zip and in the header line 33, 34.
For some reason I found the problem description easy to misunderstand, but the linked code makes it clear. In C++11 it's easy:
#define SETUP_ENUM_LENGTH(enum_type, length) \
static constexpr int enum_length(enum_type*) { return length; }
and a
for (int i = 0; i < enum_length((Enum*)0); ++i) {
in the right place. Here's a sample:
#include <iostream>
#include <functional>
#include <boost/preprocessor/variadic/size.hpp>
/**
* Macro to setup an enum completely.
* First parameter is the name, following are the states as plain text.
*/
#define DEF_ENUM(name, ...) \
enum class name : uint8_t { __VA_ARGS__ }; \
SETUP_ENUM_LENGTH(name, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__))
/**
* Once an enum class is defined, this macro makes the size publicly available.
* Needed by enum_array. Already included in DEF_ENUM.
*/
#define SETUP_ENUM_LENGTH(enum_type, length) \
static constexpr int enum_length(enum_type*) { return length; }
/**
* Function to iterate over all elements of an enum.
*/
template<typename Enum>
void enum_for_each(const std::function<void(Enum e)> &fct) {
for (int i = 0; i < enum_length((Enum*)0); ++i) {
fct(static_cast<Enum>(i));
}
}
namespace n {
DEF_ENUM(demo,u,v,w,x,y,z,a,b,c);
}
namespace m {
DEF_ENUM(demo,a=3,b=1,c=4,d=1,e=5);
}
using std::cout;
int main()
{
enum_for_each<n::demo>([](n::demo e) { cout<<int(e); });
cout<<'\n';
enum_for_each<m::demo>([](m::demo e) { cout<<int(e); });
cout<<'\n';
int ndemo[enum_length((n::demo*)0)];
int mdemo[enum_length((m::demo*)0)];
cout << sizeof ndemo << ' ' << sizeof mdemo << '\n';
}
As a side note, that static_cast<Enum>(i) looks troublesome, does it really do the right thing with the m::demo enum?
To preserve the original templated-enum_length usage and so make the array-allocation usage a bit prettier is easy from here, rename the function enum_length_helper and then
template<typename Enum>
struct enum_length {
enum result=enum_length_helper((Enum*)0);
};
If it is possible for your use-case, you could do specialization on a namespace basis and then aggregate as follows, using C++11 since you mentioned it but can work without.
Assume you have a number of namespaces ns_1 to ns_k like this:
namespace ns_i {
template<class T> struct my_int: std::integral_constant<int, -1> {};
/*...*/
enum e_1 { /*...*/ };
template<> struct my_int<e_1>: std::integral_constant<int, 101> {};
/*...*/
enum e_n { /*...*/ };
template<> struct my_int<e_n>: std::integral_constant<int, 142> {};
/*...*/
}
I assume you already have the means to do a unique numbering. Then you aggregate the my_int from all namespaces like this:
namespace your_lib {
template<
class T,
template<class> class sources... /* any number of template classes,
each taking one type argument */
>
struct Union:
std::integral_constant<int, -1> {}; // default -1 for (empty template list)
template<
class T,
template<class> class source, // match first template
template<class> class sources... // match all but first template
>
struct Union<T, source, sources...>:
std::conditional<
source::value == -1,
union<T, sources...>, // recursively call union on all but first tempalte
source // or if there's a value in first, use it
> {};
template<class T> struct my_int :
Union<T, ns_1::my_int, /*...,*/ ns_k::my_int> {};
/* here you could use boost preprocessor to iterate over the namespaces
since you mentionned it */
}
Here's a solution using functions and ADL:
#include <type_traits>
enum TypeInfo
{
Unknown = 0,
TypeA,
TypeB
};
template <TypeInfo x>
using TInfo = std::integral_constant<TypeInfo, x>;
template <class T>
TInfo<Unknown> TypeInfoFunc(T);
template <class T>
struct GetTypeInfo : decltype(TypeInfoFunc(std::declval<T>())){};
namespace a{
class A{};
TInfo<TypeA> TypeInfoFunc(A);
};
namespace b {
class B{};
TInfo<TypeB> TypeInfoFunc(B);
}
int main()
{
static_assert(GetTypeInfo<a::A>::value == TypeA, "");
static_assert(GetTypeInfo<b::B>::value == TypeB, "");
return 0;
}
The TypeInfoFunc is found using ADL meaning that it can be defined in the same namespace as the class your specializing it for.
EDIT
Based on the comments, I think I understand a bit better now. The solution doesn't change much, simply make your function:
namespace a
{
struct A{};//Or whatever class you want to hold data about your type
A TypeInfoFunc(TInfo<TypeA>);
}
and change GetTypeInfo to
template <TypeInfo x>
struct GetTypeInfo : decltype(TypeInfoFunc(TypeInfo<X>())) {};
This way you can call GetTypeInfo<TypeA> and access all the information in (in this case) class A.
you can avoid the need to specialize a structure if you move the type information in the type itself:
template <int V>
struct TypeInfo { enum { result = V, }; };
class yourClass : TypeInfo<2> //works better if you have an enum instad of number
{}
//...
static_assert(a_type::result == 2);
If you do this you will never have the problem with namespaces if the type is declared you will always have access to type info.

C++ keeping a collection of pointers to template objects, all derived from a non-template class

I have a list of object "identifiers" (a long enumeration list, with a unique value per "identifier"):
enum Identifier {
Enum0, // an identifier for a bool value
Enum1, // ... for a float value
Enum2, // ... for an int value
// etc.
};
I wish to maintain a collection of Value objects associated with these identifiers. hese Value objects contain a single value, but this value may be integer, floating point, boolean or some other (simple) type. This is in the context of managing a set of configuration values in a system. Later on I plan to extend these value types to support validation of the internal value, and relate some values to other values.
However I wish to use templates for these Value classes, because I want to write operations on these Values generically. If I were to use inheritance I would have BaseValue, then derive IntValue, FloatValue, etc. from BaseValue. Instead I have Value, Value, etc.
But I also want to store an access mechanism to each of these Values in a single collection. I want one class to instantiate all of them and maintain them in the collection. If I were using inheritance, I could use a vector of pointers to BaseValue. But because I'm using templates, these classes are not polymorphically related to each other.
So I thought about making them based on an (empty?) abstract base class that is not parameterised:
class BaseParameter {
};
template<typename T>
class Parameter : public BaseParameter {
public:
explicit Parameter(T val) : val_(val) {}
void set(ParameterSource src) { val_ = extract<T>(src); }
T get() { return val_; };
private:
T val_;
};
Note that the 'set' member function takes a "ParameterSource", which is a source of a value that is 'reinterpreted' by specific "to_type" functions. It's an API function out of my control - I have to interpret the type myself, given that I know what the type is meant to be, set below. That's what extract does - it's specialised for various T types like float, int, bool.
Then I can add them to a std::vector like this:
std::vector<BaseParameter *> vec(10);
vec[Enum0] = new Parameter<bool>(true); // this is where I state that it's a 'bool'
vec[Enum1] = new Parameter<float>(0.5); // ... or a float ...
vec[Enum2] = new Parameter<int>(42); // ... or an int ...
I know I should probably use unique_ptr but for now I'm just trying to get this working. So far this seems to work fine. But I'm wary of it because I'm not sure the full type of the instantiated templates is going to be retained at run-time.
Later I want to index the 'vec' by an arbitrary enum value, retrieve the parameter and call a member function on it:
void set_via_source(Identifier id, ParameterSource source) {
// if id is in range...
vec[id]->set(source);
}
And other code that makes use of these configuration values (and therefore knows the type) can access them with:
int foo = vec[Enum2]->get() * 7;
This seemed to work, most of the time. It compiles. I've had some odd crashes I can't explain, that tend to crash the debugger too. But I'm very suspicious of it, because I don't know whether C++ is able to determine the real type of the pointed-to object (including the parameterised type), because the base class isn't parameterised itself.
Unfortunately it seems to me that if I parameterise the base class, then I essentially remove the commonality between these Value classes that allow them to be stored in a single container.
I took a look at boost::any to see if that might help, but I'm not sure it would apply in this case.
At a higher level, what I'm trying to do is connect a vast collection of configuration items from an external source (via an API) that delivers values of different type depending on the item, and store them locally so that the rest of my code can easily access them as if they are simple data members. I also want to avoid writing a giant switch statement (because that would work).
Is this something that Type Erasure might help me with?
If you know at compile time the type associated with each enum, you can do this "easily" with boost::variant and without type-erasure or even inheritance. (Edit: The first solution uses a lot of C++11 features. I put a less-automatic but C++03 conformant solution at the end.)
#include <string>
#include <vector>
#include <boost/variant.hpp>
#include <boost/variant/get.hpp>
// Here's how you define your enums, and what they represent:
enum class ParameterId {
is_elephant = 0,
caloric_intake,
legs,
name,
// ...
count_
};
template<ParameterId> struct ConfigTraits;
// Definition of type of each enum
template<> struct ConfigTraits<ParameterId::is_elephant> {
using type = bool;
};
template<> struct ConfigTraits<ParameterId::caloric_intake> {
using type = double;
};
template<> struct ConfigTraits<ParameterId::legs> {
using type = int;
};
template<> struct ConfigTraits<ParameterId::name> {
using type = std::string;
};
// ...
// Here's the stuff that makes it work.
class Parameters {
private:
// Quick and dirty uniquifier, just to show that it's possible
template<typename...T> struct TypeList {
using variant = boost::variant<T...>;
};
template<typename TL, typename T> struct TypeListHas;
template<typename Head, typename...Rest, typename T>
struct TypeListHas<TypeList<Head, Rest...>, T>
: TypeListHas<TypeList<Rest...>, T> {
};
template<typename Head, typename...Rest>
struct TypeListHas<TypeList<Head, Rest...>, Head> {
static const bool value = true;
};
template<typename T> struct TypeListHas<TypeList<>, T> {
static const bool value = false;
};
template<typename TL, typename T, bool B> struct TypeListMaybeAdd;
template<typename TL, typename T> struct TypeListMaybeAdd<TL, T, false> {
using type = TL;
};
template<typename...Ts, typename T>
struct TypeListMaybeAdd<TypeList<Ts...>, T, true> {
using type = TypeList<Ts..., T>;
};
template<typename TL, typename T> struct TypeListAdd
: TypeListMaybeAdd<TL, T, !TypeListHas<TL, T>::value> {
};
template<typename TL, int I> struct CollectTypes
: CollectTypes<typename TypeListAdd<TL,
typename ConfigTraits<ParameterId(I)>::type
>::type, I - 1> {
};
template<typename TL> struct CollectTypes<TL, 0> {
using type = typename TypeListAdd<TL,
typename ConfigTraits<ParameterId(0)>::type
>::type::variant;
};
public:
using value_type =
typename CollectTypes<TypeList<>, int(ParameterId::count_) - 1>::type;
template<ParameterId pid>
using param_type = typename ConfigTraits<pid>::type;
// It would be better to not initialize all the values twice, but this
// was easier.
Parameters() : values_(size_t(ParameterId::count_)) {
clear(std::integral_constant<int, int(ParameterId::count_) - 1>());
}
// getter for when you know the id at compile time. Should have better
// error checking.
template<ParameterId pid>
typename ConfigTraits<pid>::type get() {
// The following will segfault if the value has the wrong type.
return *boost::get<typename ConfigTraits<pid>::type>(&values_[int(pid)]);
}
// setter when you know the id at compile time
template<ParameterId pid>
void set(typename ConfigTraits<pid>::type new_val) {
values_[int(pid)] = new_val;
}
// getter for an id known only at runtime; returns a boost::variant;
value_type get(ParameterId pid) {
return values_[int(pid)];
}
private:
// Initialize parameters to default values of the correct type
template<int I> void clear(std::integral_constant<int, I>) {
values_[I] = param_type<ParameterId(I)>();
clear(std::integral_constant<int, I - 1>());
}
void clear(std::integral_constant<int, 0>) {
values_[0] = param_type<ParameterId(0)>();
}
std::vector<value_type> values_;
};
// And finally, a little test
#include <iostream>
int main() {
Parameters parms;
std::cout << ('(' + parms.get<ParameterId::name>() + ')')<< ' '
<< parms.get<ParameterId::is_elephant>() << ' '
<< parms.get<ParameterId::caloric_intake>() << ' '
<< parms.get<ParameterId::legs>() << std::endl;
parms.set<ParameterId::is_elephant>(true);
parms.set<ParameterId::caloric_intake>(27183.25);
parms.set<ParameterId::legs>(4);
parms.set<ParameterId::name>("jumbo");
std::cout << ('(' + parms.get<ParameterId::name>() + ')')<< ' '
<< parms.get<ParameterId::is_elephant>() << ' '
<< parms.get<ParameterId::caloric_intake>() << ' '
<< parms.get<ParameterId::legs>() << std::endl;
return 0;
}
For the benefit of those who can't yet use C++11, here's a version which uses non-class enums and which is not smart enough to build the boost::variant type by itself, so you have to provide it manually:
#include <string>
#include <vector>
#include <boost/variant.hpp>
#include <boost/variant/get.hpp>
// Here's how you define your enums, and what they represent:
struct ParameterId {
enum Id {
is_elephant = 0,
caloric_intake,
legs,
name,
// ...
count_
};
};
template<int> struct ConfigTraits;
// Definition of type of each enum
template<> struct ConfigTraits<ParameterId::is_elephant> {
typedef bool type;
};
template<> struct ConfigTraits<ParameterId::caloric_intake> {
typedef double type;
};
template<> struct ConfigTraits<ParameterId::legs> {
typedef int type;
};
template<> struct ConfigTraits<ParameterId::name> {
typedef std::string type;
};
// ...
// Here's the stuff that makes it work.
// C++03 doesn't have integral_constant, so we need to roll our own:
template<int I> struct IntegralConstant { static const int value = I; };
template<typename VARIANT>
class Parameters {
public:
typedef VARIANT value_type;
// It would be better to not initialize all the values twice, but this
// was easier.
Parameters() : values_(size_t(ParameterId::count_)) {
clear(IntegralConstant<int(ParameterId::count_) - 1>());
}
// getter for when you know the id at compile time. Should have better
// error checking.
template<ParameterId::Id pid>
typename ConfigTraits<pid>::type get() {
// The following will segfault if the value has the wrong type.
return *boost::get<typename ConfigTraits<pid>::type>(&values_[int(pid)]);
}
// setter when you know the id at compile time
template<ParameterId::Id pid>
void set(typename ConfigTraits<pid>::type new_val) {
values_[int(pid)] = new_val;
}
// getter for an id known only at runtime; returns a boost::variant;
value_type get(ParameterId::Id pid) {
return values_[int(pid)];
}
private:
// Initialize parameters to default values of the correct type
template<int I> void clear(IntegralConstant<I>) {
values_[I] = typename ConfigTraits<I>::type();
clear(IntegralConstant<I - 1>());
}
void clear(IntegralConstant<0>) {
values_[0] = typename ConfigTraits<0>::type();
}
std::vector<value_type> values_;
};
// And finally, a little test
#include <iostream>
int main() {
Parameters<boost::variant<bool, int, double, std::string> > parms;
std::cout << ('(' + parms.get<ParameterId::name>() + ')')<< ' '
<< parms.get<ParameterId::is_elephant>() << ' '
<< parms.get<ParameterId::caloric_intake>() << ' '
<< parms.get<ParameterId::legs>() << std::endl;
parms.set<ParameterId::is_elephant>(true);
parms.set<ParameterId::caloric_intake>(27183.25);
parms.set<ParameterId::legs>(4);
parms.set<ParameterId::name>("jumbo");
std::cout << ('(' + parms.get<ParameterId::name>() + ')')<< ' '
<< parms.get<ParameterId::is_elephant>() << ' '
<< parms.get<ParameterId::caloric_intake>() << ' '
<< parms.get<ParameterId::legs>() << std::endl;
return 0;
}

Template metaprogram converting type to unique number

I just started playing with metaprogramming and I am working on different tasks just to explore the domain. One of these was to generate a unique integer and map it to type, like below:
int myInt = TypeInt<AClass>::value;
Where value should be a compile time constant, which in turn may be used further in meta programs.
I want to know if this is at all possible, and in that case how. Because although I have learned much about exploring this subject I still have failed to come up with an answer.
(P.S. A yes/no answer is much more gratifying than a c++ solution that doesn't use metaprogramming, as this is the domain that I am exploring)
In principle, this is possible, although the solution probably isn't what you're looking for.
In short, you need to provide an explicit mapping from the types to the integer values, with one entry for each possible type:
template< typename T >
struct type2int
{
// enum { result = 0 }; // do this if you want a fallback value
};
template<> struct type2int<AClass> { enum { result = 1 }; };
template<> struct type2int<BClass> { enum { result = 2 }; };
template<> struct type2int<CClass> { enum { result = 3 }; };
const int i = type2int<T>::result;
If you don't supply the fallback implementation in the base template, this will fail for unknown types if T, otherwise it would return the fallback value.
Depending on your context, there might be other possibilities, too. For example, you could define those numbers within within the types themselves:
class AClass {
public:
enum { inta_val = 1 };
// ...
};
class BClass {
public:
enum { inta_val = 2 };
// ...
};
// ...
template< typename T >
struct type2int
{
enum { result = T::int_val }; // will fail for types without int_val
};
If you give more context, there might be other solutions, too.
Edit:
Actually there isn't any more context to it. I was looking into if it actually was possible, but without assigning the numbers itself.
I think Mike's idea of ordering is a good way to do this (again, for a fixed set of types) without having to explicitly assign numbers: they're implicitly given by the ordering. However, I think that this would be easier by using a type list. The index of any type in the list would be its number. I think something like the following might do:
// basic type list manipulation stuff
template< typename T1, typename T2, typename T3...>
struct type_list;
// meta function, List is assumed to be some instance of type_list
template< typename T, class List >
struct index_of {
enum { result = /* find index of T in List */ };
};
// the list of types you support
typedef type_list<AClass, BClass, CClass> the_type_list;
// your meta function
template< typename T >
struct type2int
{
enum { result = index_of<T, the_type_list>::result };
};
This does what you want. Values are assigned on need. It takes advantage of the way statics in functions are assigned.
inline size_t next_value()
{
static size_t id = 0;
size_t result = id;
++id;
return result;
}
/** Returns a small value which identifies the type.
Multiple calls with the same type return the same value. */
template <typename T>
size_t get_unique_int()
{
static size_t id = next_value();
return id;
}
It's not template metaprogramming on steroids but I count that as a good thing (believe me!)
Similiar to Michael Anderson's approach but this implementation is fully standards compliant and can be performed at compile time. Beginning with C++17 it looks like constexpr values will be allowed to be used as a template parameter for other template meta programming purposes. Also unique_id_type can be compared with ==, !=, >, <, etc. for sorting purposes.
// the type used to uniquely identify a list of template types
typedef void (*unique_id_type)();
// each instantiation of this template has its own static dummy function. The
// address of this function is used to uniquely identify the list of types
template <typename... Arguments>
struct IdGen {
static constexpr inline unique_id_type get_unique_id()
{
return &IdGen::dummy;
}
private:
static void dummy(){};
};
The closest I've come so far is being able to keep a list of types while tracking the distance back to the base (giving a unique value). Note the "position" here will be unique to your type if you track things correctly (see the main for the example)
template <class Prev, class This>
class TypeList
{
public:
enum
{
position = (Prev::position) + 1,
};
};
template <>
class TypeList<void, void>
{
public:
enum
{
position = 0,
};
};
#include <iostream>
int main()
{
typedef TypeList< void, void> base; // base
typedef TypeList< base, double> t2; // position is unique id for double
typedef TypeList< t2, char > t3; // position is unique id for char
std::cout << "T1 Posn: " << base::position << std::endl;
std::cout << "T2 Posn: " << t2::position << std::endl;
std::cout << "T3 Posn: " << t3::position << std::endl;
}
This works, but naturally I'd like to not have to specify a "prev" type somehow. Preferably figuring out a way to track this automatically. Maybe I'll play with it some more to see if it's possible. Definitely an interesting/fun puzzle.
I think it is possible to do it for a fixed set of types, but quite a bit of work. You'll need to define a specialisation for each type, but it should be possible to use compile-time asserts to check for uniqueness. I'll assume a STATIC_ASSERT(const_expr), like the one in Boost.StaticAssert, that causes a compilation failure if the expression is false.
Suppose we have a set of types that we want unique IDs for - just 3 for this example:
class TypeA;
class TypeB;
typedef int TypeC;
We'll want a way to compare types:
template <typename T, typename U> struct SameType
{
const bool value = false;
};
template <typename T> struct SameType<T,T>
{
const bool value = true;
};
Now, we define an ordering of all the types we want to enumerate:
template <typename T> struct Ordering {};
template <> struct Ordering<void>
{
typedef TypeC prev;
typedef TypeA next;
};
template <> struct Ordering<TypeA>
{
typedef void prev;
typedef TypeB next;
};
template <> struct Ordering<TypeB>
{
typedef TypeA prev;
typedef TypeC next;
};
template <> struct Ordering<TypeC>
{
typedef TypeB prev;
typedef void next;
};
Now we can define the unique ID:
template <typename T> struct TypeInt
{
STATIC_ASSERT(SameType<Ordering<T>::prev::next, T>::value);
static int value = TypeInt<T>::prev::value + 1;
};
template <> struct TypeInt<void>
{
static int value = 0;
};
NOTE: I haven't tried compiling any of this. It may need typename adding in a few places, and it may not work at all.
You can't hope to map all possible types to an integer field, because there are an unbounded number of them: pointer types with arbitrary levels of indirection, array types of arbitrary size and rank, function types with arbitrary numbers of arguments, and so on.
I'm not aware of a way to map a compile-time constant integer to a type, but I can give you the next best thing. This example demonstrates a way to generate a unique identifier for a type which - while it is not an integral constant expression - will generally be evaluated at compile time. It's also potentially useful if you need a mapping between a type and a unique non-type template argument.
struct Dummy
{
};
template<typename>
struct TypeDummy
{
static const Dummy value;
};
template<typename T>
const Dummy TypeDummy<T>::value = Dummy();
typedef const Dummy* TypeId;
template<typename T, TypeId p = &TypeDummy<T>::value>
struct TypePtr
{
static const TypeId value;
};
template<typename T, TypeId p>
const TypeId TypePtr<T, p>::value = p;
struct A{};
struct B{};
const TypeId typeA = TypePtr<A>::value;
const TypeId typeB = TypePtr<B>::value;
I developed this as a workaround for performance issues with ordering types using typeid(A) == typeid(B), which a certain compiler fails to evaluate at compile time. It's also useful to be able to store TypeId values for comparison at runtime: e.g. someType == TypePtr<A>::value
This may be doing some "bad things" and probably violates the standard in some subtle ways... but thought I'd share anyway .. maybe some one else can sanitise it into something 100% legal? But it seems to work on my compiler.
The logic is this .. construct a static member function for each type you're interested in and take its address. Then convert that address to an int. The bits that are a bit suspect are : 1) the function ptr to int conversion. and 2) I'm not sure the standard guarantees that the addresses of the static member functions will all correctly merge for uses in different compilation units.
typedef void(*fnptr)(void);
union converter
{
fnptr f;
int i;
};
template<typename T>
struct TypeInt
{
static void dummy() {}
static int value() { converter c; c.f = dummy; return c.i; }
};
int main()
{
std::cout<< TypeInt<int>::value() << std::endl;
std::cout<< TypeInt<unsigned int>::value() << std::endl;
std::cout<< TypeInt< TypeVoidP<int> >::value() << std::endl;
}
I don't think it's possible without assigning the numbers yourself or having a single file know about all the types. And even then you will run into trouble with template classes. Do you have to assign the number for each possible instantiation of the class?
type2int as compile time constant is impossible even in C++11. Maybe some rich guy should promise a reward for the anwser? Until then I'm using the following solution, which is basically equal to Matthew Herrmann's:
class type2intbase {
template <typename T>
friend struct type2int;
static const int next() {
static int id = 0; return id++;
}
};
template <typename T>
struct type2int {
static const int value() {
static const int id = type2intbase::next(); return id;
}
};
Note also
template <typename T>
struct type2ptr {
static const void* const value() {
return typeid(T).name();
}
};