Expand variadic template to array of static members - c++

I've defined a base class template:
template<class actual_class>
class base
{
public:
static const int value;
}
and the definition of value depends on the actual_class tparam.
Next, I have a bunch of derived classes from base, let's call them a and b. Let's also say that a::value = 5 and b::value = 10.
Now, in a method template where I need to access the static values from a parameter pack. I'd like to have them in a vector.
template<class... derived_from_bases>
void foo(irrelevant_class<derived_from_bases...> irrelevant)
{
// std::vector<int> values = { ... }
...
}
For the function called with < a, b > tparams I'd like the values vector to look like this:
std::vector<int> values = {5 /* a::value */, 10 /* b::value */};
Also having an std::array instead of an std::vector would be a nice touch.
Thank you for your help in advance.

For a vector, you just need
std::vector<int> values = { derived_from_bases::value... };
If you have C++17, you can get a std::array the same way like
std::array values = { derived_from_bases::value... };
and CTAD will deduce the type and size of the array for you. If you do not have C++17, then you can use
std::array<int, sizeof...(derived_from_bases)> values = { derived_from_bases::value... };

Related

How do I implement return type lookup for templates using a map in c++?

I have two arrays. One containing pointers to a base class. The other containing an Enum whose values give pointers as to the real type.
I would now like to have a function that can get me an array with those entries that have a certain Enum value associated with them and return that array with the correct return type.
Example (semi pseudocode):
std::array<BaseType, 2> a = { TypeAInstance, TypeBInstance };
std::array<TypeEnum, 2> b = { TypeA, TypeB };
template<typename SearchType>
std::array<SearchType*, 2> GetEntriesOfType()
{
std::array<SearchType*, 2> ret;
for(int i = 0; i < 2; i++)
{
ret[i] = nullptr;
if(b[i] == EnumForType(SearchType)) ret[i] = a[i];
}
}
However I do not know of anyway to construct the EnumForType function or the reverse TypeForEnum function which would allow me to declare the template return type using:
template<SearchTypeEnum>
TypeForEnum(SearchTypeEnum) GetEntriesOfType();
I would like to define such a constexpr function if possible.
The underlying problem is that I have an array of Plugins that I iterate over. Where each plugin needs to be handled differently depending on the type. However since the type is runtime dependent and I can't have dynamic allocation (embedded system constraints) I store all Plugins in a fixed prereserved memory space. I need to somehow cast the pointer to the base class to its correct type during runtime and handle it accordingly.
This GetEntriesOfType function is supposed to make that more convenient. I could of course get both arrays and then do a switch statement while iterating over both but I wanted to simplify that to the end user of that library.
One way is to map each class type to an enumerator value:
class A;
class B;
enum class Types {
A,
B
};
template<class T> struct EnumForType;
template<> struct EnumForType<A> { static constexpr Types value = Types::A; };
template<> struct EnumForType<B> { static constexpr Types value = Types::B; };
int main() {
auto a = EnumForType<A>::value;
auto b = EnumForType<B>::value;
}
You would use it like:
if(b[i] == EnumForType<SearchType>::value)
The core ingredient to map an enumeration (or an integer) to a type is full specialization of something. For example, you could have
template <TypeEnum> struct EnumToType;
template <> struct EnumToType<TypeA> { using type = TypeAType; };
template <> struct EnumToType<TypeB> { using type = TypeBType; };
// ...
... and then use that appropriately in your function, e.g. (this assumes that BaseType is actually a pointer type; if it is not, the values are sliced when creating the array and the concrete type cannot be recovered portably):
std::array<BaseType, 2> a = { TypeAInstance, TypeBInstance };
std::array<TypeEnum, 2> b = { TypeA, TypeB };
template<SearchTypeEnum E>
std::array<typename EnumToType<E>::type*, 2> GetEntriesOfType() {
std::array<typename EnumToType<E>::type*, 2> rc;
for (int i = 0; i != 2; ++i) {
rc[i] = a[i] == E? static_cast<typename EnumToType<E>::type*>(b[i]);
}
return rc;
}
Instead of a enum, you could create an id for each type.
Let's define this id for a type with templates. Since you prefer constexpr, RTTI is not a option. Here's what I do when I want a type id:
template<typename>
void type_id() {}
using type_id_t = void(*)();
This is as simple as that. Each instanciated function will ahve a different address, and the address of the function will always be the same for a given type under the as if rule.
Then, adding you metadata is pretty easy. We could define your array of plugin like this:
std::array<std::pair<type_id_t, BaseType*>, 2> a = {
std::make_pair(type_id<TypeA>, typeAInstance),
std::make_pair(type_id<TypeB>, typeBInstance)
};
With that, you just associated a instance with a value that identify the type. Your GetEntriesOfType become trivial to implement:
template<typename SearchType>
std::array<SearchType*, 2> GetEntriesOfType() {
std::array<SearchType*, 2> ret;
for(int i = 0; i < 2; i++) {
ret[i] = nullptr;
if(a[i].first == &type_id<SearchType>) ret[i] = a[i].second;
}
}
I usually avoid to associate type with enums, as enums are not meant to represent type, but to map to simple integral values.
Plus, if your array is constexpr, you could even return an array with only the right size to fit all instance of the SearchType instead of filling other values with nullptr.

C++ template class with argument copy constructor

I have a c++ generic class that looks like this:
template <class T, int N> class TwoDimArray{
private:
T tda_[N][N];
int n_;
public:
TwoDimArray(T tda[][N]){
..
}
~TwoDimArray(){
..
}
..
}
How would I write a copy constructor that I will be able to call somehow like this:
int a1[][2] = { 1, 2, 3, 4 };
TwoDimArray <int, 2> tdm1(a1);
TwoDimArray tdm1Copy (tmd1); // or whatever the correct form is
I hope I was clear.
You have lots of typos in your code, but declaring a copy constructor is simple:
TwoDimArray(const TwoDimArray& rhs){/*implement it here*/}
The copy constructor is part of a template class, so you must specify the template type when copying
TwoDimArray<int, 2> tdm1Copy (tdm1); // specify <int,2> as the type
or use auto
auto tdm1Copy(tdm2);
Full working example below:
#include <iostream>
template <class T, int N> class TwoDimArray {
private:
T tda_[N][N];
int n_;
public:
TwoDimArray(T tda[][N]) {
// implement
}
~TwoDimArray() {
// implement
}
TwoDimArray(const TwoDimArray& rhs) // don't really need this
{
// implement
}
};
int main()
{
int a1[][2] = { 1, 2, 3, 4 };
TwoDimArray <int, 2> tdm1(a1);
TwoDimArray<int, 2> tdm1Copy (tdm1); // specify <int,2> as the type
}
Unless you are doing this for a homework or learning purposes, just use std::vector<std::vector<T>> to "simulate" a 2-D array.
Additional remark: Since your class contains an array (and not a pointer, they are not the same), you don't need a copy constructor. The default copy constructor does it for you, i.e. copies the array element by element. So, just get rid of the copy constructor altogether and let the compiler generate a default one.

Inheritance and array of types

I have a probably very simple question in C++.
Let's say I have class defined as:
class PS
{
private:
int value;
int m;
public:
PS();
PS(int value, int m);
PS(int m);
};
Now I want to define an array with elements of this type. For example,
PS t[3];
However, I want all of the elements in this array to have m=2. How would I do that? I am assuming I have to use inheritance some how, right?
So for example I don't want to do something like this:
>PS t[3];
>t[0].PS(2);
>t[1].PS(2);
>t[2].PS(2);
I want to do it one show for all elements of t.
Using your constructor, you can simply use brace initialization :
PS t[] = { PS(2) , PS(2), PS(2) };
Or as suggested by #0x499602D2, since PS has a non explicit constructor, simply :
PS t[] = { 2, 2, 2 };
I would also suggest you to use std::array<> instead of C-style arrays.
It is not really safe not to initialize a value but you can use the C++11 feature that allows you to initialize variable directly in your class definition :
class PS {
private:
int value;
int m = 2;
public:
PS() {};
};
If you are using an older version of C++ you can consider overloading the default constructor
class PS {
private:
int value;
int m;
public:
PS(int _m = 2) : m(_m) {};
};
The STL Vector class is preferred to c-arrays. Using one, you could do:
std::vector<PS> t(3, PS(2));

Pointer to array as template parameter

I'd like to instanciate my object with template parameters only. One of the parameters is a pointer to array, and I'm looking for the correct syntax.
const MyCustomType* array[2] = { &object1, &object2 };
OBJ1 < 10, 10, array > myobj1;
Below, a sample of the class OBJ1.
template < int a, int b, /* help ! */ >
class OBJ1
{
public:
OBJ1();
~OBJ1();
private:
//methods
};
What's the right syntax to use the third template parameter ? Is that even possible ?
For an array of type int of size n the syntax is
int (*paramname)[N];
Or with a helper type alias
template<typename T>
using type = T;
Then
type<int[N]> *paramname;
In your case you pass a pointer to the arrays first element though, and not a pointer to the array. You need to prefix the array name with & to do the latter when passing the array.

Factory method returning an concrete instantiation of a C++ template class

I have a class
template <unsigned int N>
class StaticVector {
// stuff
};
How can I declare and define in this class a static factory method returning a StaticVector<3> object, sth like
StaticVector<3> create3dVec(double x1, double x2, double x2);
?
"How can I declare and define in this class"
In what class? You've defined a class template, not a class. You can't call a static function of a class template itself, you have to call a particular version of the static function that's part of a real class.
So, do you want the template (and hence all instantiations of it) to have a function returning a StaticVector<3>, or do you want one particular instantiation of that template to have a function returning a StaticVector<3>?
If the former:
template <unsigned int N>
struct SV {
int contents[N];
static SV<3> get3dVec(int x, int y, int z) {
SV<3> v;
v.contents[0] = x;
v.contents[1] = y;
v.contents[2] = z;
return v;
}
};
int main() {
SV<3> v = SV<1>::get3dVec(1,2,3);
}
works for me.
If the latter (you only want get3dVec to be a member of SV<3>, not of all SV<whatever>), then you want template specialisation:
template <unsigned int N>
struct SV {
int contents[N];
};
template<>
struct SV<3> {
int contents[3]; // must be re-declared in the specialization
static SV<3> get3dVec(int x, int y, int z) {
SV<3> v;
v.contents[0] = x;
v.contents[1] = y;
v.contents[2] = z;
return v;
}
};
int main() {
SV<3> v = SV<1>::get3dVec(1,2,3); // compile error
SV<3> v = SV<3>::get3dVec(1,2,3); // OK
}
If for no other reason than to make the calling code look nicer by omitting the basically irrelevant template parameter, I agree with Iraimbilanja that normally a free function (in a namespace if you're writing for re-use) would make more sense for this example.
C++ templates mean that you don't need static functions as much in C++ as you do in Java: if you want a "foo" function that does one thing for class Bar and another thing for class Baz, you can declare it as a function template with a template parameter that can be Bar or Baz (and which may or may not be inferred from function parameters), rather than making it a static function on each class. But if you do want it to be a static function, then you have to call it using a specific class, not just a template name.
Something like :
template< unsigned int SIZE >
StaticVector< SIZE > createVec( const std::tr1::array< double, SIZE >& values )
{
StaticVector< SIZE > vector;
for( int i = 0; i < values.size(); ++i ) // here assuming that StaticVector have [] operator to access values on write
{
vector[i] = values[i];
}
return vector;
}
... or a variant would certainly work. (did'nt test it)
Usage would be:
std::tr1::array< double, 3 > vectorValues = { 10.0, 10.0, 42.0 };
StaticVector<3> vector1 = createVector( vectorValues ); // if the compiler can guess the size from the array information
StaticVector<3> vector2 = createVector<3>( vectorValues ); // if you have to specify the size
An alternative would be to replace std::tr1::array by a basic array but it would be using raw arrays at your own risks :)
First, i believe you originally mean to return
StaticVector<N>
Instead of always a specialization with N==3 . So, what you want to do is writing it like this:
template <unsigned int N>
class StaticVector {
public:
// because of the injected class-name, we can refer to us using
// StaticVector . That is, we don't need to name all template
// parameters like StaticVector<N>.
static StaticVector create3dVec(double x1, double x2, double x2) {
// create a new, empty, StaticVector
return StaticVector();
}
};
If you really want to always return a 3dVector, you would probably want to restrict it to N==3, so that for example StaticVector<4>::create3dVec doesn't work. You can do that using the technique described here.
If you want to have a function like createVec that works with any size, you probably want to replace the parameters with an array. You can do otherwise, but that's advanced and requires some macro tricks applied with boost::preprocessor. It's not worth it i think. The next C++ version will provide variadic templates for this purpose. Anyway,consider using something like this:
I think it would only complicate this unnecessarily here. A quick solution is to use a boost::fusion::vector instead, putting it into the class template instead of the version above:
static StaticVector createVec(double (&values)[N]) {
// create a new, empty, StaticVector, initializing it
// with the given array of N values.
return StaticVector();
}
You could use it with
double values[3] = {1, 2, 3};
StaticVector<3> v = StaticVector<3>::createVec(values);
Note that it accepts an array by reference. You can't give it a pointer. That is because it matches the use of parameters: You couldn't provide less or more arguments for the other way too. It will also protect you from cases like this:
// oops, values is a null pointer!
StaticVector<3> v = StaticVector<3>::createVec(values);
An array never can be a null pointer. Of course if you like, you can always change the array parameter to a pointer. It would just be my personal preference :)
#litb
I wanted to return a 3-element vector. The reason is that these vectors are supposed to be geometric entities, so there is no need for N to be too high (I'm not a string physicist, so I don't work in 11-dimensional spaces). I wanted to create a template to avoid duplicating code but keep 1-, 2- and 3-dimensional vectors as separate types.