Specialize an object

A

Andrea Crotti

Supposing I have something like this:

class Base
{
Base() {
if external condition true
make it extended!
}
}

class Extended : public Base
{
...

}

so in the Base constructor I want to check one condition and in case
make the object more "powerful".

What's a good way to do it?
From my attempts maybe the only clean way is to create also a class
"Simple" and create a Simple or Extended object depending on the
condition, but also it's a bit overkill maybe..
 
G

Gert-Jan de Vos

Supposing I have something like this:

class Base
{
        Base() {
               if external condition true
                  make it extended!
         }

}

class Extended : public Base
{
        ...

}

so in the Base constructor I want to check one condition and in case
make the object more "powerful".

Make a factory function:

std::auto_ptr<Base> CreateBase()
{
if (external_condition)
return std::auto_ptr<Base>(new Extended());
return std::auto_ptr<Base>(new Base());
}

It is indeed usually better to also make the Simple case a separate
derived class from Base. In this case you can later modify/extend the
Simple case without changing the Extended case also.
 
A

Andrea Crotti

Gert-Jan de Vos said:
Make a factory function:

std::auto_ptr<Base> CreateBase()
{
if (external_condition)
return std::auto_ptr<Base>(new Extended());
return std::auto_ptr<Base>(new Base());
}

It is indeed usually better to also make the Simple case a separate
derived class from Base. In this case you can later modify/extend the
Simple case without changing the Extended case also.

Ok thanks, but there another problem.
That object I create is not a normal object, it's like the "main" of the
whole program.

So I should in short substitute the running "this" with an instance of
the Simple or Extended class, that's why it gets ugly...
 
A

Andrea Crotti

Andrea Crotti said:
Ok thanks, but there another problem.
That object I create is not a normal object, it's like the "main" of the
whole program.

So I should in short substitute the running "this" with an instance of
the Simple or Extended class, that's why it gets ugly...

No news?
Suppose I'm in that situation as before, what if I do something like:

class Base
{
Base() {
// construction of the object
bool is_extended = external_check();
if (is_extended) {
Extended e(this);
this = e;
}
}
}

class Extended : public Base
{
...

}


would work or is just bad and ugly?
 
A

Andrea Crotti

Andrea Crotti said:
No news?
Suppose I'm in that situation as before, what if I do something like:

class Base
{
Base() {
// construction of the object
bool is_extended = external_check();
if (is_extended) {
Extended e(this);
this = e;
}
}
}

class Extended : public Base
{
...

}


would work or is just bad and ugly?


Ok that was just bad, I've seen it doesn't work at all.
The problem as I said is that the constructor of my object is like the
main of the program, because it's a library which is called by something
like

extern "C" Node* create(ostream &out)
{
return new Node(out);
}

otherwise it would be quite easy to have a factory function with an
auto_ptr, but like this how do I manage it?
 
R

Ruslan Mullakhmetov

Ok that was just bad, I've seen it doesn't work at all.
The problem as I said is that the constructor of my object is like the
main of the program, because it's a library which is called by something
like

extern "C" Node* create(ostream&out)
{
return new Node(out);
}

otherwise it would be quite easy to have a factory function with an
auto_ptr, but like this how do I manage it?

as i see you made at least 2 errors:

(i) you are creating LOCAL object e and returning pointer to it by
assigning this (which would be discussed later) is wrong, cause when
control flow leaves the scope (in this case this is if(extended){...} )
local object is destructed and pointer would point to nothing which
means UB. You can solve this error by creating Extended in dynamic
memory with new operator. like

Extended *pe = new Extended( this );

(ii) you are trying to assign to this..... if i'm not mistaken it is
const pointer to 'this' object. you can not make assigning, you'll
immediately get error from compiler.

as for you question i think you make things much more complicated.
Ok that was just bad, I've seen it doesn't work at all.
The problem as I said is that the constructor of my object is like the
main of the program, because it's a library which is called by something
like

extern "C" Node* create(ostream &out)
{
return new Node(out);
}

what prevent you to put factory inside create(), assuming that
implementation of create() in you power.

otherwise if library itself create instance of class with predefined
name you can make it as wrapper, proxy for you real class and put
factory as Gert-Jan de Vos suggested you in constructor.

i see it like this. also i toke into account note of Gert-Jan de Vos
It is indeed usually better to also make the Simple case a separate
derived class from Base. In this case you can later modify/extend the
Simple case without changing the Extended case also.

and splited Base class to ImplBase which is actual base and Simple class
responsible for simple non-extended logic.

class Node {

class ImplBase // may be abstract
{
virtual ~ImplBase(){}
};
class Simple : public ImplBase {...};
class Extended : public ImplBase {...};

std::auto_ptr<ImplBase> _pimpl;

std::auto_ptr<ImplBase> create_impl()
{
if (external_condition)
return std::auto_ptr<ImplBase>(new Extended());
return std::auto_ptr<ImplBase>(new Simple());
}
public:
Node( ostream out ):_pimpl( create_impl() )
{

}
};

hope that helps.

BR, RM
 
A

Andrea Crotti

what prevent you to put factory inside create(), assuming that
implementation of create() in you power.

otherwise if library itself create instance of class with predefined
name you can make it as wrapper, proxy for you real class and put
factory as Gert-Jan de Vos suggested you in constructor.

Ok then the create() is in my power, but there is a problem.
My code generates a shared library which then is loaded (via dlopen) by
some other code, that actually calls the right symbols.

Now this process sets a "environment*" variable, which I have available
in the constructor of the Node but not in the create() (for some
reasons I don't get).

Without access to that I can't really do anything unfortunately, so
things are a bit harder.
I need to understand maybe how the extern "C" declaration diverges from
the Node constructor in the execution path to solve this...
Any hint?
and splited Base class to ImplBase which is actual base and Simple
class responsible for simple non-extended logic.

class Node {

class ImplBase // may be abstract
{
virtual ~ImplBase(){}
};
class Simple : public ImplBase {...};
class Extended : public ImplBase {...};

std::auto_ptr<ImplBase> _pimpl;

std::auto_ptr<ImplBase> create_impl()
{
if (external_condition)
return std::auto_ptr<ImplBase>(new Extended());
return std::auto_ptr<ImplBase>(new Simple());
}
public:
Node( ostream out ):_pimpl( create_impl() )
{

}
};

hope that helps.

BR, RM

This looks nice, I'll give it a try if there are no other solutions in
the create()..

Thansk
 
A

Andrea Crotti

Ruslan Mullakhmetov said:
and splited Base class to ImplBase which is actual base and Simple
class responsible for simple non-extended logic.

class Node {

class ImplBase // may be abstract
{
virtual ~ImplBase(){}
};
class Simple : public ImplBase {...};
class Extended : public ImplBase {...};

std::auto_ptr<ImplBase> _pimpl;

std::auto_ptr<ImplBase> create_impl()
{
if (external_condition)
return std::auto_ptr<ImplBase>(new Extended());
return std::auto_ptr<ImplBase>(new Simple());
}
public:
Node( ostream out ):_pimpl( create_impl() )
{

}
};

hope that helps.

BR, RM


another thing, this looks nice but how do I use it?
I mean in the end is in create() that I have to return an object, which
then is used by the rest of the code.

In this case I could not realliy do it anyway, unless I use a static
function which then I think will have the same problem as I wrote above
(the "environment" variable not reachable).
 
R

Ruslan Mullakhmetov

Ok then the create() is in my power, but there is a problem.
My code generates a shared library which then is loaded (via dlopen) by
some other code, that actually calls the right symbols.

Now this process sets a "environment*" variable, which I have available
in the constructor of the Node but not in the create() (for some
reasons I don't get).

i did not get in full. How "environment*" variable is available at
Node{} constructor and IS NOT available in create() function? It looks
misterious for me?

correct me if i understand something wrong.

let you library be named mylib.dll which exports.
Node* create(ostream &) and ext app that load your library is
extapp.exe (you are coding for win, right?)

so,

(i) extapp.exe loads mylib.dll
(ii) extapp.exe prepares "environment*" variable
(iii) extapp.exe calls create()
(iv) mylib.dll creates instance of Node class using "environment*"
variable.

Which address space (app or library) magic "environment*" variable is
prepared in?
(i) if in extapp's address space then how it pass "environment*"
variable to Node* constructor?
(ii) if in library's space then why it's available only in Node
constructor?

to go any further I need to sort it out.

Without access to that I can't really do anything unfortunately, so
things are a bit harder.
I need to understand maybe how the extern "C" declaration diverges from
the Node constructor in the execution path to solve this...
Any hint?

what do you mean under "diverges in the execution path" and "execution
path"? unfortunately i didn't catch it. are "execution path" and
"control flow" the same?
 
R

Ruslan Mullakhmetov

another thing, this looks nice but how do I use it?
I mean in the end is in create() that I have to return an object, which
then is used by the rest of the code.

you could do very bad thing as _thick_ interface and _forward_ all
requests to implementation inside. but this is ugly solution.

also you could allow access to implementation like

Simple& Node::get_simple();
Extended& Node::get_extended();

is it acceptable?

seems there is something wrong in design because you are trying to
operate with Node* as different classes. if interfaces of Simple and
Extended are different then you will need to check in all code whether
you instance points to simple or extended class and do any sort of casting.

try to redesign.

BR, RM
 
A

Andrea Crotti

Ruslan Mullakhmetov said:
i did not get in full. How "environment*" variable is available at
Node{} constructor and IS NOT available in create() function? It looks
misterious for me?

correct me if i understand something wrong.

let you library be named mylib.dll which exports.
Node* create(ostream &) and ext app that load your library is
extapp.exe (you are coding for win, right?)

so,

(i) extapp.exe loads mylib.dll
(ii) extapp.exe prepares "environment*" variable
(iii) extapp.exe calls create()
(iv) mylib.dll creates instance of Node class using "environment*"
variable.

Which address space (app or library) magic "environment*" variable is
prepared in?
(i) if in extapp's address space then how it pass "environment*"
variable to Node* constructor?
(ii) if in library's space then why it's available only in Node
constructor?

to go any further I need to sort it out.

It's not easy to understand for me either, anyway I found it:
node->environment = inetSimulationEnvironment;

And then that code calls
node->initialize, which is where I should do all my stuff...

In one of the two possible environments first a node is created, and
then this variable is set.
I'm not even sure if they're passing through the create(), because I
can't see any call to it, but they should

It's a bit a mystery for me too, but another thing, why when I debug I
can
- see calls from library X
- see calls from my library Y
- and then I can't go in library X anymore

I even tried to set breakpoints on the correct names in X but nothing I
can't debug them anymore (using g++ and gdb).
what do you mean under "diverges in the execution path" and "execution
path"? unfortunately i didn't catch it. are "execution path" and
"control flow" the same?

Yes the same..
 
J

James Kanze

Supposing I have something like this:
class Base
{
Base() {
if external condition true
make it extended!
}
}
class Extended : public Base
{
...
}
so in the Base constructor I want to check one condition and in case
make the object more "powerful".
What's a good way to do it?

The usual way is to have a factory function. You could also use
the strategy pattern, with the extended functionality in the
strategy implementation---this would allow extending the
functionality even after the instance has been constructed.
 
J

James Kanze

What does that change?

You want to change the type dynamically? It's not possible.
Use the strategy pattern instead.
No news?
Suppose I'm in that situation as before, what if I do something like:
class Base
{
Base() {
// construction of the object
bool is_extended = external_check();
if (is_extended) {
Extended e(this);
this = e;

This won't compiler, since "this" is a pointer, and e isn't. If
you write "*this = e", then you slice. (Supposing that Base is
assignable---generally, classes designed to be used as bases
aren't.)
class Extended : public Base
{
...
}
would work or is just bad and ugly?

Since what you've written won't compile, it won't work.

As already explained, there are two solutions: a factory
function, or the strategy pattern. If you need to change the
behavior after the object has been constructed (not in the
constructor), you need the strategy pattern.
 
F

Fred Zwarts

Andrea Crotti said:
Supposing I have something like this:

class Base
{
Base() {
if external condition true
make it extended!
}
}

class Extended : public Base
{
...

}

so in the Base constructor I want to check one condition and in case
make the object more "powerful".

What's a good way to do it?
From my attempts maybe the only clean way is to create also a class
"Simple" and create a Simple or Extended object depending on the
condition, but also it's a bit overkill maybe..

Maybe by using a wrapper class with a pointer to Base.
In the wrapper constructor create either a Base or an Extended
(depending on the external condition)
and put its address in the pointer.
All member functions of the wrapper class use corresponding functions
of Base or Extende via this pointer.
 
A

Andrea Crotti

Fred Zwarts said:
Maybe by using a wrapper class with a pointer to Base.
In the wrapper constructor create either a Base or an Extended
(depending on the external condition)
and put its address in the pointer.
All member functions of the wrapper class use corresponding functions
of Base or Extende via this pointer.

Only one (big) problem, from further investigations I have that quite
funnily I can only decide what object to construct AFTER the object is
already constructed, and I'm in "initialize".

So I guess that the only way is that this constructed class is a thick
class that then dispatch to the right thing...

auto_ptr<Base> otherClass;

Then in initialize I set it and for every call I should do
otherClass->realCall

It's a bit annoying but I don't see other ways..
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,145
Messages
2,570,824
Members
47,369
Latest member
FTMZ

Latest Threads

Top