Background
For an embedded project, I want a class that takes a list of structs. This list is known at compile-time, so I shouldn't have to resort to dynamic memory allocation for this.
However, how do I make a struct/class that encapsulates this array without having to use its size as a template parameter?
Templates
My first idea was to do exactly that:
struct Point {
const uint16_t a;
const double b;
};
template<size_t n>
struct Profile {
Array<Point, n> points;
Profile(const Array<Point, n> &points) : points(points) {}
};
Here, Profile is the class that stores/encapsulates the array of points (the 2-member structs). n, the size of the array, is a template parameter.
I'm using this implementation of Array, similar to std::array, btw, because I don't have access to the STL on this embedded platform.
However, no I have another class that uses this Profile that now also has to be templated because Profile is templated with the size of the array:
template<size_t n>
class Runner {
private:
const Profile<n> profile;
public:
Runner(const Profile<n> &profile) : profile(profile) {};
void foo() {
for(auto point : profile.points) {
// do something
}
}
};
As can be seen, this Runner class operates on a Profile and iterates over it. Having to template Runner is not that much of an issue by itself, but this Runner in turn is used by another class in my project, because this other class calls Runner::foo(). Now I have to template that class as well! And classes that use that class, etc.
That's getting out of hand! What started with just one template parameter to specify the size, now propagates through my entire application. Therefore, I don't think this is a good solution.
Question
Is there a way to 'hide' the size of the array in Profile or Runner? Runner only needs to iterate over it, so the size should in principle only affect its implementation, not its public interface. How would I do that, though?
Also, can I avoid having to manually specify n at all, and just pass an array to Profile's constructor and let the compiler figure out how big it is? At compile-time, of course. I feel like this should be possible (given this array is known at compile-time), but I don't know how exactly.
Other approaches
Macros
I could write a macro like
#define n 12
and include that in both the Profile.h and the place where I instantiate a Profile. This feels dirty though, I and would like to avoid macros.
Vector
I could avoid this fuss by just using a std::vector (or equivalent) instead, but that is allocated at run-time on the heap, which I would like to avoid here since it shouldn't be necessary.
Is there a way to 'hide' the size of the array in Profile or Runner?
Yes. The solution is indirection. Instead of storing the object directly, you can point to it. You don't need to know the size of what you're pointing at.
A convenient solution is to point into dynamic storage (for example std::vector) because it allows you to "bind" the lifetime of the dynamically sized object to a member. That's not necessary in general, and you can use automatic storage instead. However, in that case you cannot bind the lifetime of the pointed object, and you must be very careful to not let the pointed object be destroyed before you stop using it.
The indirection can be done at whatever level you prefer. If you do it at the lowest level, you simply store the array outside of Profile. In fact, if all that profile does is contain an array, then you don't need a class for it. Use a generic span:
struct Runner {
span<const Point> profile;
void foo() {
for(auto point : profile) {
// do something
}
}
};
Point points[] {
// ... number of points doesn't matter
};
Runner runner {
.profile = points,
};
By span, I mean something like std::span. If you cannot use the standard library, then use another implementation. It's basically just a pointer and size, with convenient template constructor.
To clarify, you can pick any two, but you cannot have all three of these:
Lifetime of the array bound to the class (safe)
No compiletime constant size
No dynamic storage
1,2 (no 3) = std::vector, RAII
1,3 (no 2) = std::array, templates, no indirection
2,3 (no 1) = std::span, be careful with lifetimes
I'll expand on this comment:
The idea is that Runner takes Profiles no matter their size. Runner needs to iterate over it, but apart from that, its behaviour is always the same. The class using Runner and calling Runner::foo() doesn't need to know the size. The problem with templating Runner is that the class using Runner also needs to be templated, and the classes using that, etc.
This is only a problem when the class is using the templated Runner directly. It has more dependencies than it actually needs. If it doesn't need to know about the size of the array, then it should not know about the size of the array. If runtime polymorphism is an option you can add a base class that allows accessing the array elements, but doesn't need to know anything about the arrays size. The following is only a sketch:
#include <iostream>
struct RunnerInterface {
virtual int* begin() = 0;
virtual int* end() = 0;
virtual ~RunnerInterface(){}
};
template <unsigned size>
struct Runner : RunnerInterface {
int data[size];
int* begin() override { return data; }
int* end() override { return data+size; } // pointer one past the end if fine (it won't get dereferenced)
};
void foo(RunnerInterface& ri) {
for (auto it = ri.begin(); it != ri.end(); ++it){
*it = 42;
}
}
void bar(RunnerInterface& ri){
for (auto it = ri.begin(); it != ri.end(); ++it){
std::cout << *it;
}
}
int main() {
Runner<42> r;
foo(r);
bar(r);
}
Now if a class needs a Runner member, they store a std::unique_ptr<RunnerInterface> and only on construction you need to decide for the size of the array (though you still need to decide for the size somewhere).
The field has to be immutable so I can't use the vector. Is there a way to do it like in the title?
I want to do something like this:
typedef list<pair<int,string>> list_pair;
class tree{
private:
list_pair arr[]{
public:
tree(int size){
arr[size];
}
}
Is there a way to do it like in the title?
No.
A non-static member array must have a known size, there is no way around that in C++.
The field has to be immutable so I can't use the vector.
Your example array of non-const isn't immutable either.
Furthermore, I don't see a reason why that should matter. It's a private member, so it's fairly easy to choose to not mutate it. That way the class remains effectively immutable from the outside. Conclusion: Use std::vector.
So I've got a class that I'm generalizing into a base class. One of the member variables is a 2D array of a struct.
struct SomeType
{
...
}
and then in the class's header:
SomeType member_variable_ [SIZE_ONE][SIZE_TWO];
But, in my situation, SIZE_TWO needs to be set when the class is initialized because it's going to be different depending on what's using this. What's the best way to have a 2D struct array with a size that's not yet set as a member variable?
The simplest way to solve it is to not use C-style arrays at all, but to use std::vector. Or possibly an std::array of vectors:
std::array<std::vector<SomeType>, SIZE_ONE> member_variable_;
Now you can easily insert as many (or as few) SomeType objects as needed, and still use the array-indexing syntax:
member_variable_[some_index][some_other_index]
To set a fixed size at runtime for the "second" Dimension, you can do something like this in the constructor:
for (auto& v : member_variable_)
v = std::vector<SomeType>(the_runtime_size);
You could use a template:
template<unsigned SIZE_TWO>
class theClass
{
SomeType member_variable_ [SIZE_ONE][SIZE_TWO];
SIZE_TWO will be set when you instantiate the class.
theClass<5> tc; //member_variable_ [SIZE_ONE][5];
You could also use containers like std::vector or std::array.
I'm trying to create an anonymous union that holds a generic vector in order to use it as a member in a class without naming the type_name of the union itself.
So that I could call the vector inside the class as following:
vec.size();
But my approach
template <typename T>
union{
std::vector<T> vec;
};
will only give me the error "template class without a name". This also happens with structures. So does it not like to be anonymous when it is generic?
Search results just gave me the option to create a generic vector inside a named structure but, besides I couldn't get this to work either, I would loose the benefits of the anonymous union und I would need to call the vector e.g. as
struct_name.vec.size();
or even
class_name.struct_name.vec.size();
which I tried to avoid.
I want to make the vector generic so that it can store integers or doubles and I don't need to declare two different vectors with their own specific data types. Beside learning some principles of generics I also aim for lesser declarations and storage usage with this technique.
You cannot reliably do what you want. You need at least some way to discriminate at runtime if you have a vector of int or a vector of float.
With C++11 you might code
class StrangeVector {
bool has_int;
union {
std::vector<int> vint;
std::vector<float> vfloat;
};
public:
StrangeVector(bool withints) : has_int(withints) {
if (withints) new(&vint) std::vector<int>();
else new(&vfloat) std::vector<float>();
}
~StrangeVector() {
if (has_int) vint.~vector<int>();
else vfloat.~vector<float>();
}
};
But such code is really bad smelling. (I would suggest using a union of pointers, perhaps of smart pointers e.g. std::shared_ptr or std::unique_ptr, or perhaps std::optional; see this).
See also this, or boost::any ...
Notice that except for RTTI typing information is used at compile time in C++.
What I have:
Vectors of different custom structs(one custom struct per vector)
I used pointers to each struct to give it a static size per record
A vector that combines these vectors via a pointer to each
When I try to cast a pointer to the custom vector it fails on every iteration
My workaround is a call to a function that takes the vector pointer as an argument and returns a void pointer.
I know this is wrong despite being functional, but I can't find a reference on the right way to define the cast method properly.
I'm looking for the right way to accomplish this.
typedef struct mystruct {
DWORD something;
vectorclassa * somethinga;
vectorclassb * somethingb;
};
typedef std::vector<mystruct*> amystruct;
void * theWrongWay(mystruct * apointer){
return apointer;
}
typedef std::vector bigvector;
If I try to bigvector.push_back(&instance_of_amystruct)
It fails. If I change std::vector<amystruct*> bigvector to a void* and call theWrongWay with a &amystruct instance, it compiles/runs. It just seems wrong.
The problem is that I don't know how to define the missing method for vector or cast it to something vector knows how to deal with without doing something...bad.
It's very hard to answer this question because it's hard to tell why you want to do this. Why do you want to keep vectors of different types in another vector?
Anyway, I'll just assume that you want to do this because there's some common action you want to take on each vector. Then you can simply define an abstract class that defines that action, and have a templated child class of this class that keep a different vector depending on the template argument. You can then keep the vectors in a container referring to them as their common ancestor. Some code:
class ActionableVector {
virtual void doSuperCoolStuff() = 0;
}
template<typename T>
class VectorHandler: public ActionableVector {
vector<T> handledVector;
// vector<T> & handleVector; // You can keep a reference to an external vector too
virtual void doSuperCoolStuff() {
//do super cool stuff in a type-safe manner
}
}
template<>
class VectorHandler<ATypeThatNeedsSpecialAttention>: public ActionableVector {
Vector<ATypeThatNeedsSpecialAttention> handledVector;
virtual void doSuperCoolStuff() {
// Do especially cool stuff
}
vector<ActionableVector*> myVectors;
for(ActionableVector * av: myVectors ) { //C++ 11 yaay
av->doSuperCoolStuff();
}
If you really really really want to keep objects of completely different types in a container, and willing to sell your soul to the devil for that, look at this answer.