I would like to know if the following scenario is possible.
I have a class A which has some data members, one of them being a container (let's assume a simple array).
Now to the constructors of A, I'd like to pass a lambda function to initialize the data members of the class.
I want the user to be given the ability to write his own initialization routine and have it applied on the class.
For example, consider this constructor of A which takes 2 arguments.
The first as the size of the array and the second, a lambda function.
A(std::function<void (self_type)> f) {
f(*this);
}
Now I'd create an object as follows:
A a([](A a) {
//do something here to initialize array and data members
});
This way, the user is given the ability to perform his own initialization rather than using the constructors or other fill functions offered by the class.
Is this possible using some trickery? It'd be even better if I somehow could use this inside the lambda function.
For example -
A a([]() {
//do something here to initialize array and data members
this->_n = 100; // where _n is some data member.
});
Any possible solution is welcome!
Generally, if a class keeps members private, it enforces an invariant over the state of those members. If you let J. Random User poke at your privates you don't know that he will maintain that invariant. So I'm not sure if this is design-wise a good idea.
That said, you can achieve something like this by putting all relevant data members in a private base class as public members:
struct A_base {
int something;
std::string some_other_thing;
};
class A : private A_base {
public:
A(std::function<void(A_base&)> f) { f(*this); }
template <typename Function>
A(Function&& f) { std::forward<Function>(f)(*this); }
};
This provides the required access to the user function, but obviously without the syntactic ability to use this.
Related
Let's assume I have a class A looking like that:
class A {
public:
public A(bool someFlag, Params someParams);
private:
vector<string> texts;
}
I would like to extract the logic of initializing the texts member field.
I came up with 2 ideas:
First idea - static, private member functions that would return the desired vectors of texts.
A::A(bool someFlag, Params someParams) {
if (someFlag)
texts = createSomeTexts(someParams);
else
texts = createOtherTexts(someParams);
}
Second idea - private member functions that would assign the desired vectors of texts to the class members by themselves.
A::A(bool someFlag, Params someParams) {
if (someFlag)
createAndAssignSomeTexts(someParams);
else
createAndAssignOtherTexts(someParams);
}
Of course both versions do the job correctly, but I wonder what is the advised approach to theese situations. Also, if the approach should change if member initalization requires more parameters (especially ones that are stored in the class as members as well).
You should strive to initialize your data member, not assign to them in the constructor body. Both versions you showed cause default construction of the std::vector<std::string> instance, and assign to it later. Hence, I'd suggest this:
A::A(bool someFlag, const Params& someParams) :
texts(someFlag ? createSomeTexts(someParams) : createOtherTexts(someParams))
{}
or, more readable, let createSomeTexts handle the flag, too:
A::A(bool someFlag, const Params& someParams) :
texts(createSomeTexts(someFlag, someParams))
{}
Make createSomeTexts a member function if it needs to access other data members (make sure they're declared before the texts member and properly initialized - as #Scheff pointed out, this is unlikely to be a good idea, though). Otherwise, make it a free function (see here why this is preferrable). Once createSomeTexts is a free function, you could equally well construct the object like this:
std::vector<std::string> stringsToInject = createSomeText(/* Some flags.... */);
A instance(stringsToInject); // A's ctor updated to make this work
which could further separate concerns as the constructor of A takes care of initializing the data members, while the logic to create the initialization data is located somewhere else.
I would use case 1 because the functions createSomeTexts and createOtherTexts do not alter any class variables. That means these functions can be unit tested.
It is better not to use global variables and if you must, not to alter them from the global scope (this->) but to pass them by reference or as a pointer to your function.
This way you can pass stubs in your code and write test cases.
Also, Params should be a const reference:
class A {
public:
public A(const bool someFlag, const Params &someParams);
private:
vector<string> texts;
}
I need to instantiate many objects from a class, but each one of them needs to be aware of a certain value X that is common for every object of this class, like a global parameter. This is necessary for my constructors to work properly in my objects.
Is there a way to do that without passing the value as a constructor parameter? What I wanna do is use the same variable in all objects so I don't waste RAM.
*in my real situation it's not just an X value, is a 1024-dimmension int vector.
What you want is a static member. "When a data member is declared as static, only one copy of the data is maintained for all objects of the class". e.g.
class myClass {
public:
static int x;
};
I assume you mean that you want a vector of size 1024 as the shared variable across all your classes. You could do this:
class MyClass {
static std::vector<int> s_my_vector;
}
This code would go into your header file. In your cpp file, you'd have to initialize the std::vector. However, I do not recommend this. Class static variables that require constructor calls (i.e. not primitives or POD types) have a lot of gotchas that I'm not planning to go into. I will offer a better solution however:
class MyClass {
static std::vector<int> & GetMyVector()
{
static std::vector<int> my_vector;
static bool initialized = MyVectorInit(my_vector);
return my_vector;
}
static bool MyVectorInit(std::vector<int> & v)
{
v.resize(1024);
...
}
public:
MyClass() {
std::vector<int> & v = GetMyVector();
...
}
static void EarlyVectorInit()
{
GetMyVector();
}
}
In this case, the static local variable ensures that there will only be one copy of my_vector, and you can get a reference to it by calling GetMyVector. Furthermore, the static bool initialized is guaranteed to only be created once, which means that MyVectorInit will only be called once. You can use this method in case you need to populate your vector in some non-trivial way that can't be done in the constructor.
The way I've written it, your vector will be created automatically the first time you need to use it, which is fairly convenient. If you want to manually trigger creation for some reason, call EarlyVectorInit().
I wrote a class where the constructor is private.
I need to assign the given value to the private members ONLY ONCE
in the method construct(int a).
It should be something like a constructor but not a constructor !
Every time this construct(int a) is called after the first time,
I do not need to reassign anything to that specific OBJECT.
How to achieve that without any booleans?
I thought of boost::call_once but it calls construct(int a) once for ENTIRE CLASS! and I need to call this function ONCE for EACH OBJECT.
just like ctor! Any ideas?
UPDATE 1:
The Constructor is private. But the class has some members those values can be assigned from the outside but only ONCE
I am trying to achieve some automatisation for checking if a function was called or not already without using bool wasCalled or something like that.
UPDATE 2:
LT::Pointer lut = LT::New();
std::vector<double> points;
....
lut->construct(points);
The second time
lut->construct(points);
is called - error should be given, or just somehow make it impossible.
Direct Answer:
You can devise a wrapper that applies "assign-once" semantics to the wrapped object.
However, you can not make the compiler detect that a value is being set for the second time at compile time, so you should be prepared to make it assert/throw at runtime.
Background/look around
As others have said, this smells very much like a design flaw. Why can't you have the New operation forward constructor parameters (a-la make_shared, make_unique?):
template <typename T, typename... Args>
SmartPointer<T> genericNew(Args&&... args) {
return SmartPointer<T>(new T(std::forward<Args>(args)...));
}
Of course, there could be specialized factory methods that even know how to set private properties after construction. Make the factory methods friends, to preven others from using the hidden property (setters) after creation by the factory:
struct X {
int a;
X(int i) : a(i) {}
typedef SmartPointer<X> Ptr;
static Ptr New(int a, int init_only) {
Ptr p(new X(a));
p->init_only = init_only;
return p;
}
private:
int init_only;
};
(here I opted to make the New factory method a static member, so it's implicitly a friend)
I have this problem again and again... and still have not a satisfactory answer...
Especially when I put the class into a container, later on I need to record more information on every element in the container during a specific processing, but after processing I do not need the extra information anymore....
I often found some libraries try to solve the above situation by defining a void* in their data structure to provide user-defined data structure extension. Just the same described in this Q&A.
But it produces memory / resource handling problem... and other problems that I feel this approach is error-prone.
In the modern day of object-oriented programming, I am thinking of
using inheritance & polymorphism. Use base class's pointer in the container, but then I have to add derived class's accessor into the base class. It is kind of strange...
is there any other better ways to extend a class's property while maintain container comparability in C++?
The best way to store extra data about a object without actually compromising the integrity of the object itself is to store a pair of data in the container instead.
struct User { ... };
struct ExtraData { ... };
typedef std::pair<User, ExtraData> UserAndExtraData;
Now I can create a container type in C++ which stores both pieces of information together without compromising the independence of either type.
std::vector<UserAndExtraData> vector;
I would look into the Decorator Pattern. You can decorate your objects while processing them then throw the decorated objects away. If there is a lot of shared data you can also look into the FlyWeight pattern.
"User" could be extended by template parameters. for example,
template <typename... Extra>
struct User : Extra...
{
...
};
struct ExtraData {...};
struct ExtraExtraData {...};
using ExtraUser = User<ExtraData>;
using MoreExtraUser = User<ExtraData, ExtraExtraData>;
In the modern day of object-oriented programming, I am thinking of
using inheritance & polymorphism. Use base class's pointer in the
container, but then I have to add derived class's accessor into the
base class. It is kind of stange...
you don't need to put a pointer to your derived class in your base class when using inheritance. You just need to cast to the derived class. the problem is getting your data into the derived objects when it's stored in the base objects - you can only cast them if they were created as the derived type, even if your collection holds them as the base type. (if they are created as the derived type, then just cast!)
So if you have a collection of BaseC, you can create a new class DerivedC that has a copy constructor that takes a BaseC. You can copy your BaseC object into it, perform your processing on the DerivedC objects and then copy these back into a BaseC object for storage. This uses the Flyweight pattern. Note that if you have a collection of BaseC objects, you cannot just pretend they are DerivedC classes as they will not have the storage to hold all the data members, you need to create new DerivedC objects.
Alternatively, create a new class just for processing that contains a (smart pointer) reference to your base class objects, copy the reference in, perform the processing, delete the processing objects when you're done.
If your objects are in a vector, then a simple approach is to make a parallel vector:
void doSomething(const vector<MyObject>& my_objects)
{
vector<ExtraData> extra_data;
int n_objects = extra_data.size();
extra_data.reserve(n_objects);
for (int i=0; i!=n_objects; ++i) {
extra_data.push_back(calcExtraData(my_objects[i]));
}
// now use my_objects[i] and extra_data[i] together.
// extra data goes away when the function returns.
}
You don't have to modify your original objects, and it is very efficient.
If you have some other container type, you can use a map:
void doSomething(const set<MyObject>& my_objects)
{
map<MyObject*,ExtraData> extra_data;
set<MyObject>::const_iterator i=my_objects.begin(), end=my_objects.end();
for (;i!=end;++i) {
extra_data[&*i] = calcExtraData(*i);
}
// now use extra_data[&obj] to access the extra data for obj.
// extra data goes away when the function returns.
}
this isn't as efficient as with vectors, but you still don't have to modify your original classes.
However, it becomes more difficult to maintain the parallel structures if the underlying container can change during the processing.
One simple option is to add a type parameter representing the "extra data"...
template<class ExtraDataType>
struct MyExtensibleContainer
{
...
ExtraDataType extra;
};
Perhaps if you indicate why this solution isn't sufficient, the true requirements will come through.
Example for int and void*:
struct IntOrVoid
{
};
struct IntOrVoid1 : IntOrVoid
{
int x;
};
struct IntOrVoid2 : IntOrVoid
{
void* x;
};
typedef shared_ptr<IntOrVoid> PIntOrVoid;
then use MyExtensibleContainer<PIntOrVoid>
or altenatively:
union IntOrVoid
{
int x_int;
void* x_voidp;
};
then use MyExtensibleContainer<IntOrVoid>
The problem you are describing has nothing to do with adding an "extra" data type. The problem you are describing has to do with holding a variant type that can have one of many hetrogeneous types. There are many ways to do this, it is a much more general problem.
I am creating a bunch of C structs so i can encapsulate data to be passed over a dll c interface. The structs have many members, and I want them to have defaults, so that they can be created with only a few members specified.
As I understand it, the structs need to remain c-style, so can't contain constructors. Whats the best way to create them? I was thinking a factory?
struct Foo {
static Foo make_default ();
};
A factory is overkill. You use it when you want to create instances of a given interface, but the runtime type of the implementation isn't statically known at the site of creation.
The C-Structs can still have member functions. Problems will, however, arise if you start using virtual functions as this necessitates a virtual table somewhere in the struct's memory. Normal member functions (such as a constructor) don't actually add any size to the struct. You can then pass the struct to the DLL with no problems.
I would use a constructor class:
struct Foo { ... };
class MakeFoo
{
Foo x;
public:
MakeFoo(<Required-Members>)
{
<Initalize Required Members in x>
<Initalize Members with default values in x>
}
MakeFoo& optionalMember1(T v)
{
x.optionalMember1 = v;
}
// .. for the rest option members;
operator Foo() const
{
return x;
}
};
This allows to arbitrary set members of the struct in expression:
processFoo(MakeFoo(1,2,3).optionalMember3(5));
I have an easy idea, here is how:
Make the structure, just like you normally would, and create a simple function that initializes it:
struct Foo{...};
void Default(Foo &obj) {
// ... do the initialization here
}
If you have multiple structures, you are allowed in C++ to overload the function, so you can have many functions called 'default', each initializing its own type, for example:
struct Foo { //... };
struct Bar { //... };
void Default(Foo &obj) {...}
void Default(Bar &obj) {...}
The C++ compiler will know when to call the first or the second overload based on the parameter. The & makes obj a reference to whatever parameter you give it, so any changes made to obj will be reflected to the variable you put as parameter.
Edit:
I also have an idea for how to specify some parameters, you can do it by using default parameters. This is how it works:
For example you the following function; you can specify default values for parameters like this:
void Default (Foo &obj, int number_of_something = 0, int some_other_param = 10)
{ ... }