Arbitrary number of parameter assignment with template class - c++

I would like to implement a simple template ObjectPool kind of class.
It has a fixed size vector as a member variable, which elements' are default initialized upon creation.
That's done already and works fine.
The question is what is the best way to assign new elements to the member vector?
I implemented add_old_way member function, which is able to add any type of data to the ObjectPool, and works fine.
My only problem is that i create an extra object which i pass to that function, and inside, i just throw it away after assignment. Waste of resources, if we are talking about giant classes.
How should i implement add_new_way function which is able to take arbitrary parameters and assign them individually to vector element?
Please note ObjectPool is templated so any kind of class can be it's type, not just Bar or Matrix2x2 as in the example below.
I don't eve now the key words here to look it up on the internet, neither if it's possible at all.
Thank you!
template <class T>
class ObjectPool
{
vector<T> mObjects;
ObjectPool() : mObjects(size)
{
}
//that's what i could come up with to add an object to the object pool
void add_old_way(const T& object)
{
...
mObjects[nextFreeIndex] = object;
...
}
//desired way of adding object
void add_new_way(...)
{
mObjects[nextFreeIndex].param1 = param1;
mObjects[nextFreeIndex].param2 = param2;
...
mObjects[nextFreeIndex].paramN = paramN;
}
};
class Bar
{
Bar(int x, string s)
{
mX = x;
mS = s;
}
int mX;
string mS;
};
int main()
{
ObjectPool<Bar> v;
ObjectPool<Matrix2x2> v;
//that's what i could come up with
v.add_old_way(cBar(1,"asdf"));
v.add_old_way(cMatrix2x2(1,2,3,4));
//desired way of adding object
v.add_new_way(1,"asdf");
v.add_new_way(1,2,3,4);
}

Utilize move semantics and perfect forwarding to make the assignment cheap:
template <typename... Args>
void add_new_way(Args&&... args)
{
mObjects[nextFreeIndex] = T{std::forward<Args>(args)...};
}
Live on Coliru
Since the object being assigned from is a temporary, the move-assignment operator of the object being assigned to will be invoked if it exists. This will let the object transfer ownership of any expensive-to-copy resources from the temporary to the object in the pool.
For further reading, take a look at this question: What are move semantics?

If using C++11 and above, to implement your add_new_way method, you can use variadic template parameters and forwarding:
template <typename... Args>
void add_new_way(Args... args) {
mObjects.emplace_back(std::forward<Args>(args)...);
}
Then calling code can do:
v.add_new_way(arg1, arg2, ..., argN);

Related

The element without default constructor in the custom template container

I need to create a simple template container, which can store any object of any type and could be used everywhere. So, I did something like this:
template <typename Type>
class Container {
public:
Container() : arraySize(10) { valueWrappers = new Type[arraySize];}
Container(const Container& other) { /* --- */}
~Container() { /* --- */}
Container& operator=(const Container& other) { /* --- */}
/* some functions */
private:
int arraySize;
Type* valueWrappers;
};
Now I have the problem - when I'm trying to create my container using as template a class without default constructor, the compilation error appears:
class MyClass {
public:
MyClass(int value) :v(value) { }
private:
int v;
};
int main() {
Container<MyClass> cont;
return 0;
}
C2512 'MyClass': no appropriate default constructor available
The problem is that I need to initialize the array of "Type" values with something, but I don't no what I need to use. I can't use NULL because, in this case, Container will work only with pointers. So, can somebody give an advice, how am I able to do it? Or, maybe, there is another way to solve this task?
Based on your requirements, I think you're going to have to use placement new. Since you haven't provided all the relevant code, I'm going to do what I can.
First, you're going to have to allocate raw memory instead of using new directly.
Container() : arraySize(10) { valueWrappers = reinterpret_cast<Type*>(::operator new(sizeof(Type) * arraySize)); }
Now when you put something in your Container, you'll have to construct it in place, using something like the following:
new (valueWrappers + index) Type(arguments to type);
In your destructor, you'll need to explicitly call the destructors on any object that you used placement new for.
valueWrappers[index]->~Type();
Lastly, release the memory using ::operator delete.
::operator delete(valueWrappers);
Please bear in mind that this is a very quick and dirty answer, and this code can be hard to debug and maintain. You're going to have to keep track of what indexes in valueWrapper have been initialized and which haven't during cleanup. If possible, I highly recommend using something akin to std::vector, which handles all this complexity for you.
One option is to not allocate the array in the default constructor, but initialise valueWrappers to null instead. Another option is to not have a default constructor in your template. Third option is to keep your class as-is and simply document that the template is default constructible only if the type argument is default constructible.
You can use std::optional to defer initialization, which is guaranteed to handle object lifetime correctly. Letting a default constructed container have 10 elements is also a questionable choice — a (count) constructor may be preferable.
template <typename Type>
class Container {
using elem_t = std::optional<Type>;
std::size_t count{};
std::unique_ptr<elem_t[]> elems{};
public:
Container() = default;
Container(std::size_t cnt)
: count{cnt}
, elems{std::make_unique<elem_t[]>(cnt)}
{
}
// for example
template <typename... Args>
void construct_at(std::size_t pos, Args&&... args)
{
assert(pos < count);
assert(!elems[pos]);
elems[pos].emplace(std::forward<Args>(args)...);
}
// ...
};
Note that I used std::unique_ptr to simplify memory management; a pointer will also be OK, though apparently more error-prone. Now you can traverse the container and construct the elements:
class MyClass {
public:
MyClass(int value) :v(value) { }
private:
int v;
};
int main()
{
Container<MyClass> cont(10);
for (std::size_t i = 0; i < 10; ++i) {
cont.construct_at(i, /* argument */);
}
}

Trying to push_back into a vector pointing to an abstract class

Compiling my code that contains this class:
class Dessin
{
private:
vector<Figures*>T;
public:
void ajouteFigure(const Figures& f) const
{
for(auto element: T)
{
T.push_back(f);
}
}
};
yields an error:
[Error] no matching function for call to
'std::vector::push_back(const Figures&) const'
This is what I'm supposed to do in the main()
Dessin s;
s.ajouteFigure(Cercle(1.1));
Why wouldn't this work?
Assuming Cercle is a class name, you're trying to push a value where a pointer is expected.
To "fix" the error you should change your ajouteFigure prototype to accept Figures pointers and non-const this:
void ajouteFigure(Figures* f)
Then you should call it passing a pointer to a Figures object, i.e. created with a new expression:
s.ajouteFigure(new Cercle(1.1));
That being said, this code seems pointless. You're adding the pointer as many times as you have elements in the vector (which is always 0 in the example you provided).
Using raw pointers is also unadvised, you should use smart pointers like std::unique_ptr, although that would break the current code.
Consider this, less improper, example:
class Dessin
{
private:
vector<unique_ptr<Figures>> T;
public:
void ajouteFigure(unique_ptr<Figures> f)
{
T.push_back(move(f)); // just once
}
};
and at the call site:
Dessin s;
s.ajouteFigure(make_unique<Cercle>(1.1)); // C++≥14
or, if you can't use C++14:
Dessin s;
s.ajouteFigure(unique_ptr<Figures>(new Cercle{1.1}));
Just to add to this, I think you would be better to make it a template function and create the right object inside the function with arguments to the constructor passed as function parameters.
This way you don't have to create a std::unique_ptr or use new every time you call the function.
Here's a basic implementation:
class Dessin{
public:
template<typename T, typename ... Args>
void ajouteFigure(Args &&... args){
figures.emplace_back(new T(std::forward<Args>(args)...));
}
private:
std::vector<std::unique_ptr<Figures>> figures;
};
Then using the class is less error-prone:
int main(){
Dessin d;
d.ajouteFigure<Cercle>(1.1);
}

Class template with variadic constructor; storing parameters into std::vector< std::shared_ptr<> >

I'm trying to design a class template where its constructor has a variadic constructor and each parameter type must be the same data type. I would then like to store all parameters into a std::vector< std::shared_ptr<T> > container. If no parameters are passed in then the very first shared_ptr<> in the vector<> will be set and stored as a nullptr. Also I would like to be able to keep track of how many parameters are passed in. So far this is what I have tried.
declaration
template<typename T>
class Nodes {
private:
unsigned m_numParams;
std::vector<sstd::shared_ptr<T>> m_vNodes;
public:
explicit Nodes( T... ); // Not sure how to set first parameter as being default to nullptr along with the variadic syntax.
};
definition
template<typename T>
Nodes<T>::Nodes( T... ) {
if ( /* no parameters passed in or 1st parameter is nullptr */ ) {
T* ptr = nullptr;
m_vNodes.push_back( ptr );
} else {
for each ( /* parameter with same data type */ ) {
std::shared_ptr<T> sptr( new T() );
m_vNodes.push_back( sptr );
}
}
m_numParams = m_vNodes.size();
}
My understanding of basic templates is decent, but my use of variadic functions or templates are limited and I'm trying to learn new methods to further improve my overall skill set within the c++ language. I would like to know if my initial approach is logically the correct path; if this concept is possible to achieve and if so, what would be the most efficient way to implement this so when I use this class template this is what I would be expecting to and to actually have happen as in this example case below:
#include "SomeHeader.h"
int main() {
int x = 3;
int y = 5;
int z = 7;
Nodes<int> someNodes( x, y, z );
return 0;
}
The internal nodes private member variable would have shared_ptr<int> where
Nodes::m_vNodes[0] = address of x with value 3 stored
Nodes::m_vNodes[1] = address of y with value 5 stored
Nodes::m_vNodes[2] = address of z with value 7 stored
and if no parameters are passed as in...
#include "SomeHeader.h"
int main() {
Nodes<int> someNodes();
return 0;
}
the internal member variable would have a ''nullptr` stored in the vector[0] location. Would I need to also implement its default constructor Nodes(); Or is this achievable all from a single constructor?
Once I have this constructor properly working then I would have no problem moving forward to write the functions or methods to add, remove, retrieve, store, arrange or sort the data, compare the data, or manipulate the data. I've been searching here and there and haven't seem to find anything relevant to what I'm looking for. Any tips, advice, help, links or examples would be truly appreciated and I kindly thank you in advance.
initializer_list seems a better choice, but with variadic template, it should be something like:
template<typename T>
class Nodes {
private:
unsigned m_numParams;
std::vector<std::shared_ptr<T>> m_vNodes;
public:
Nodes() : m_numParams(0), m_vNodes{nullptr} {}
template <typename ... Ts>
explicit Nodes( Ts&&...ts) :
m_numParams(sizeof...(Ts)),
m_vNodes{std::make_shared<T>(std::forward<Ts>(ts))...}
{}
};
with initializer_list:
explicit Nodes(std::initializer_list<T> ini) :
m_numParams(ini.size())
{
for (auto&& e : ini) {
m_vNodes.push_back(std::make_shared<T>(e));
}
}
Demo

passing arument list to a constructor through a wrapper in c++

I want to write a wrapper function to which I can pass constructor initialization parameters for some class object. This wrapper will then use this list and pass on to constructor while defining a new object of that class. Not sure what STL or any other data type to use for this purpose. Basically in below code what data type should be used for argument_list. Any library which can assist?
Some code for perspective
class abc{
int a_;
char b_;
public:
abc(int a, char b)
:a_(a), b_(b)
{
}
}
// Wrapper
template <typename T>
void create_object_wrapper(argument_list list) // assuming argument_list can
//hold the parameters as
//intended
{
abc v1(list); //The use case I want
}
int main()
{
create_object_wrapper(1,'a'); // initialize object with (1, 'a')
return 0;
}
Thanks in advance.
use variadic perfect forwarding:
template <typename... Args>
void create_object_wrapper(Args&&... list)
{
abc v1(std::forward<Args>(list)...);
}
Use template parameter packs:
template <typename... Args>
void create_object_wrapper(Args... list)
{
abc v1(list...);
}
This will fail to compile if called with arguments which result in an invalid abc constructor call.
If you want to create the object instantly from the arguments provided, then follows sehe's answer.
If you want to store the arguments provided to create one or more objects later, then you should use a std::tupel. Follow the answers given in this question to see how this can be done. Ideally you want to use C++14 for that (see my answer to that question).

Declare inherited templatized objects and push them back into vector - elegant way?

I'm trying to declare a lot of templatized Derived<T> objects that inherit from Base, and pushing them back in a std::vector<Base*>.
struct Base { ... };
template<typename T> struct Derived : Base { /* ctor(const string&) */ ... }
Derived<bool> online{"online"};
Derived<bool> official{"official"};
Derived<bool> noRotation{"no_rotation"};
Derived<bool> noBackground{"no_background"};
Derived<int> noSound{"no_sound"};
Derived<string> noMusic{"no_music"};
Derived<bool> blackAndWhite{"black_and_white"};
vector<Base*> configValues{&online,
&official,
&noRotation,
&noBackground,
&noSound,
&noMusic,
&blackAndWhite};
As you can see, the code is horrible. Is there any way to automate this without passing the vector as a const& in the Derived<T>::Derived<T>(...) constructor?
By automate I mean avoiding the repetition of the objects' names. I would like to fill a std::vector with all my Derived<T> objects without having to list them all manually.
(Macros accepted, but hopefully there's a better solution)
So lifetime is going to be one of the problems you face, and how this pattern is used in your program is another influence to the solution.
So supposing your Derived<> instances are not owned by the vector, then you need to ensure they outlive it. There are three basic approaches, which could be combined in some cases.
The first: Create a class which stores and populates the vector. If you have the same set of Derived<> types or parameters duplicated, this can at least 'deduplicate' your common structures. Then you can give those member functions to return or populate the vectors.
The second is to use std::tuple. Tuples could be useful if there are many parameter list variants and you want one instance to store all those Derived instances while having a way to create common routines (like filling the vector):
typedef std::tuple<
Derived<bool>,
Derived<bool>,
Derived<bool>,
Derived<bool>,
Derived<bool>,
Derived<int>,
Derived<std::string>,
Derived<bool>
> atuple;
atuple t{
"online",
"official",
"no_rotation",
"no_background",
"no_sound",
"no_music",
"black_and_white"
};
const size_t size(std::tuple_size<atuple>::value);
/* or you could use std::array because the size is constant. */
std::vector<Base*>configValues;
configValues.reserve(size);
push(t,configValues);
And push() would look like:
template<std::size_t I = 0, typename V, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
push(std::tuple<Tp...>& t, V&)
{ }
template<std::size_t I = 0, typename V, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
push(std::tuple<Tp...>& t, V& vec)
{
vec.push_back(&std::get<I>(t));
push<I + 1, V, Tp...>(t, vec);
}
(borrowed from iterate over tuple).
If you don't face this problem in multiple parts of your program, then these solutions will not be so useful to you.
The third - use arrays:
std::array<Derived<bool>,5> a{{{"online"},{"official"},{"no_rotation"},{"no_background"},{"black_and_white"}}};
Derived<int> noSound{"no_sound"};
Derived<string> noMusic{"no_music"};
vector<Base*> configValues{&noSound,&noMusic};
for (Derived<bool>& b:a) configValues.push_back(&b); // original order not retained
There seems to be two separate questions here...
The first question appears be that you do not want to pass a constant std::string to the constructor of Derived<T>. The only reason I can think of to do this is if the string needs to be modified while constructing a Derived<T> object. If you do not need to modify the string I recommend taking it by const reference like you are currently doing.
If you do need to modify the string in the constructor you can change the parameter to pass the string by either rvalue reference or by value.
Derived(std::string&& str) { /*...*/ } // Pass by r-value reference
Derived(std::string str) { /*...*/ } // Pass by value
Both will allow you to modify the string during construction.
As for the second question in you comment...
To fill the vector in the way described in your comment you can use uniform initialization. The only caveat is that you will need to use dynamic storage duration for the objects added to the vector.
std::vector<Base*> configValues{
{
new Derived<bool>{"online"},
new Derived<bool>{"official"},
new Derived<bool>{"no_rotation"},
new Derived<bool>{"no_background"},
new Derived<int>{"no_sound"},
new Derived<std::string>{"no_music"},
new Derived<bool>{"black_and_white"}
}
};
I'm not sure what your objective is so how you decide to manage the lifetime of those objects is up to you. I highly recommend using a smart pointer to manage their lifetime and ownership. For instance if you require shared ownership you could use std::shared_ptr like in the following example.
std::vector<std::shared_ptr<Base>> configValues{
{
std::shared_ptr<Base>(new Derived<bool>{"online"}),
std::shared_ptr<Base>(new Derived<bool>{"official"}),
std::shared_ptr<Base>(new Derived<bool>{"no_rotation"}),
std::shared_ptr<Base>(new Derived<bool>{"no_background"}),
std::shared_ptr<Base>(new Derived<int>{"no_sound"}),
std::shared_ptr<Base>(new Derived<std::string>{"no_music"}),
std::shared_ptr<Base>(new Derived<bool>{"black_and_white"})
}
};
Since std::initializer_list requires objects to be copyable you cannot use std::unique_ptr to manage the lifetime of the objects.
It sounds like you're going for a factory-pattern type of implementation, with a common container that all derived objects get added to.
Here's a working example to get you started (ideone):
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
#include <iostream>
struct Base
{
typedef std::shared_ptr<Base> SharedPtr;
virtual ~Base(){}
virtual void print(){}
template<class T>
static SharedPtr makeDerived(const std::string& name);
static std::vector<SharedPtr> objs;
};
std::vector<Base::SharedPtr> Base::objs;
template<class T>
struct Derived : public Base
{
Derived(const std::string& name):name(name){}
void print(){std::cout<<name<<std::endl;}
std::string name;
};
template<class T>
Base::SharedPtr Base::makeDerived(const std::string& name)
{
SharedPtr p = std::make_shared<Derived<T> >(Derived<T>(name));
objs.push_back(p);
return p;
}
int main()
{
Base::makeDerived<bool>("online");
Base::makeDerived<int>("users");
std::for_each(Base::objs.begin(),Base::objs.end(),
[](Base::SharedPtr p){p->print();} );
}
shared_ptr ought to help with memory management. You can go with a static container or make an object to hold all your container, doesn't make a big difference.
Thanks for the answers, upvoted them all.
In the end I decided to create a "helper" class that dealt with ownership and creation.
From my GitHub SSVUtilsJson repo:
class Manager
{
private:
ssvu::MemoryManager<Base> memoryManager;
// MemoryManager internally has a vector<Base*> of owned pointers
public:
template<typename T> Derived<T>& create() { return memoryManager.create<Derived<T>>(); }
// MemoryManager::create<T> creates and stores a 'new T*' and returns a reference to it
};
Usage (from my GitHub SSVOpenHexagon repo)
Manager lvm;
auto& online (lvm.create<bool>());
auto& official (lvm.create<string>());
auto& noRotation (lvm.create<int>());
auto& noBackground (lvm.create<double>());
auto& noSound (lvm.create<char>());
auto& noMusic (lvm.create<void>());
for(Base* b : lvm.getItems()) { /* do something */ }