I am looking for a library that provides containers like std::array
(compile-time fixed size, but without support for aggregate initialization) and std::vector
(variable size, continuous memory) that work for types that are non-copyable and not default-constructible.
Specifically, I want to be able to pass a set of functions to the constructor that are used to obtain the arguments for the constructors of the contained objects. Something like emplace_back
, but as a constructor and using lazily evaluated arguments.
Here is a (naturally not working) example:
class stubborn_type : boost::noncopyable {
public:
explicit stubborn_type(int value)
: value(value)
{}
private:
const int value;
};
struct generate_values {
generate_values(int initial_value = 0)
: current_value(initial_value)
{}
int operator()() {
return current_value++;
}
private:
int current_value;
};
/* This should create a vector containing 10 elements initialized with the values
[0..9] in order. */
magic::vector<stubborn_type> data(10, generate_values());
I need the to solution to be compatible with C++03 (since this means no variadic templates I would prefer the Boost approach of using preprocessor magic to generate overloads for different numbers of arguments, but a reasonable fixed limit is fine as well). Does something like this exist? If not, are there any libraries that would help achieve that goal (for instance, Boost.In Place Factory is almost useful, but it does not support lazy arguments).
I do not know any ready solution, however, it is not so hard to do it. The outline of the array may look like this
#include <cstddef>
typedef char saum;
// smallest addressable unit of memory
namespace magic {
template<class T>
class IArray
{
T *const _ptr;
const size_t _length;
public:
operator T* () {
return _ptr;
}
size_t length() const {
return _length;
}
private:
IArray<T>(T* ptr, size_t length)
: _ptr(ptr), _length(length)
{}
template<class S, size_t length>
friend class Array;
};
template<class T>
class Generator
{
public:
virtual void operator() (T* place) = 0;
};
template<class T, size_t length>
class Array
{
saum buffer[sizeof(T) * length];
public:
Array(Generator<T>& generate) {
for (size_t i = 0; i < length; i++)
generate((T*)buffer + i);
}
~Array() {
for (size_t i = 0; i < length; i++)
((T*)buffer)[i].~T();
}
operator IArray<T> () {
return IArray<T>((T*)buffer, length);
}
operator T* () {
return (T*)buffer;
}
};
}
I'm little tired, so forgive me not thinking out some better names. Nevertheless, it shall do the job. Maybe, the generator solution is not too beautiful, but still gives flexibility. I believe more feathers won't case a problem.
An example
#include <new>
#include <iostream>
class Stubborn
{
public:
Stubborn(int value) : value(value*2) { }
const int value;
private:
Stubborn(const Stubborn&);
Stubborn& operator=(const Stubborn&);
};
struct StubbornGen : public magic::Generator<Stubborn>
{
StubbornGen() : current_value(0)
{}
void operator() (Stubborn* place) {
new(place) Stubborn(current_value++);
}
private:
int current_value;
};
void f(magic::IArray<Stubborn> stubs)
{
std::cout << stubs[0].value << stubs[1].value
<< stubs[2].value << stubs[3].value
<< " " << stubs.length() << std::endl;
}
int main(int argc, char *argv[])
{
StubbornGen gen;
magic::Array<Stubborn, 4> stubs(gen);
f(stubs);
}
Edit: Corrected, thanks to DyP, plus the generator tweak.
It is important to define a copy constructor and an assignment operator for the Array, to avoid the problem pointed by DyP. -- How? - depends on what you want. Making them private is a one way. And doing a non-shallow copy is an another. - In the second case, the mechanism of template instantiation should prevent from errors, when applying a non-copiable class to the Array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With