Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Should you perform complex tasks in the constructor?

Reply
Thread Tools

Should you perform complex tasks in the constructor?

 
 
Chicken McNuggets
Guest
Posts: n/a
 
      01-10-2013
I've seen various arguments against this primarily centring on the fact
that the only way to return errors in a constructor is to throw an
exception. Of course exceptions seem to be a somewhat controversial
subject in the C++ community so I'll try and avoid touching on that.

But I have a couple of classes that are created when the program
launches and are freed just before the program terminates. It makes
sense for me to put a lot of logic in the constructor as these classes
are initialised from data in different configuration files which are
passed as command line arguments.

Is there any reason I shouldn't put all the file loading / reading /
storing of data in the constructor? Or would you not consider this a
problem? For reference I'm using libxml2 for reading the data in the
configuration files.
 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      01-10-2013
On 1/10/2013 4:54 PM, Chicken McNuggets wrote:
> I've seen various arguments against this primarily centring on the fact
> that the only way to return errors in a constructor is to throw an
> exception. Of course exceptions seem to be a somewhat controversial
> subject in the C++ community so I'll try and avoid touching on that.
>
> But I have a couple of classes that are created when the program
> launches and are freed just before the program terminates. It makes
> sense for me to put a lot of logic in the constructor as these classes
> are initialised from data in different configuration files which are
> passed as command line arguments.
>
> Is there any reason I shouldn't put all the file loading / reading /
> storing of data in the constructor? Or would you not consider this a
> problem? For reference I'm using libxml2 for reading the data in the
> configuration files.


IMHO your question is a bit too general, which basically means the
answer to it is "there can be lots of things". For instance, putting
too much processing into constructors of some global data objects can
stretch the startup of a UI-driven application, and that's usually
frowned upon. At the same time if the application cannot function
without those objects fully set up, then it has to complete the
initialization whether it's in the constructor or elsewhere, so in that
case it probably doesn't really matter... What other reasons are there?
I can probably think of several. What if after some time you decide
to introduce some other initialization, and it has to be done before
your initial procedure? Do you create another global object and
construct that (and put the new initialization there)? You can run into
the problems connected to the order of things being initialized at
different times on different runs. What if that initialization has to
be conditional or have parameters that it reads from some other place?
So, think of maintenance of your code, not just of writing it... There
are probably other ones as well.

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
 
 
 
Greg Martin
Guest
Posts: n/a
 
      01-10-2013
On 13-01-10 01:54 PM, Chicken McNuggets wrote:
> I've seen various arguments against this primarily centring on the fact
> that the only way to return errors in a constructor is to throw an
> exception. Of course exceptions seem to be a somewhat controversial
> subject in the C++ community so I'll try and avoid touching on that.
>
> But I have a couple of classes that are created when the program
> launches and are freed just before the program terminates. It makes
> sense for me to put a lot of logic in the constructor as these classes
> are initialised from data in different configuration files which are
> passed as command line arguments.
>
> Is there any reason I shouldn't put all the file loading / reading /
> storing of data in the constructor? Or would you not consider this a
> problem? For reference I'm using libxml2 for reading the data in the
> configuration files.


In cases where it makes sense I set error variables to be tested, just
like a returned value, after the constructor completes.

e.g.
File *f = new File (path);
if (!f->success ()) {
std::cerr << f->error () << std::endl;
// cleanup
}



 
Reply With Quote
 
Öö Tiib
Guest
Posts: n/a
 
      01-11-2013
On Thursday, 10 January 2013 23:54:21 UTC+2, Chicken McNuggets wrote:
> I've seen various arguments against this primarily centring on the fact
> that the only way to return errors in a constructor is to throw an
> exception. Of course exceptions seem to be a somewhat controversial
> subject in the C++ community so I'll try and avoid touching on that.


It is widespread misconception that the only way to fail construction is to
throw exceptions. Alternative is to construct a "bad" object. To make it
possible to construct "bad" object the class has to be designed to allow
such states (like "obsolete", "failed", "unknown", "broken", "invalid",
"unusable", etc.). Such states may be useful (when natural) or may cause
complications (when otherwise pointless). When there are natural "bad"
states then it is not needed to throw exceptions from constructors.

> But I have a couple of classes that are created when the program
> launches and are freed just before the program terminates. It makes
> sense for me to put a lot of logic in the constructor as these classes
> are initialised from data in different configuration files which are
> passed as command line arguments.


Actually all the objects should be valid after construction. If there is
some "half-made" or "uninitialized" state after construction then that
is generally even less natural than outright "failed" states.

> Is there any reason I shouldn't put all the file loading / reading /
> storing of data in the constructor? Or would you not consider this a
> problem? For reference I'm using libxml2 for reading the data in the
> configuration files.


One issue that I have seen is when static object or static data
member of class has complex constructor that may throw. If it throws
then the program crashes when starting. Other issue that I have seen
with long-running constructors is when the program later realizes that
the object is not needed for anything aftrer all.

Generally I tend to prefer complex constructors. For example when
it is some "request" class and these have to be always sent somewhere
anyway then I usually tend to merge its construction and sending both
into constructor. That makes it simpler to use the class.
 
Reply With Quote
 
Chicken McNuggets
Guest
Posts: n/a
 
      01-11-2013
On 10/01/13 22:08, Victor Bazarov wrote:
> On 1/10/2013 4:54 PM, Chicken McNuggets wrote:
>> I've seen various arguments against this primarily centring on the fact
>> that the only way to return errors in a constructor is to throw an
>> exception. Of course exceptions seem to be a somewhat controversial
>> subject in the C++ community so I'll try and avoid touching on that.
>>
>> But I have a couple of classes that are created when the program
>> launches and are freed just before the program terminates. It makes
>> sense for me to put a lot of logic in the constructor as these classes
>> are initialised from data in different configuration files which are
>> passed as command line arguments.
>>
>> Is there any reason I shouldn't put all the file loading / reading /
>> storing of data in the constructor? Or would you not consider this a
>> problem? For reference I'm using libxml2 for reading the data in the
>> configuration files.

>
> IMHO your question is a bit too general, which basically means the
> answer to it is "there can be lots of things". For instance, putting
> too much processing into constructors of some global data objects can
> stretch the startup of a UI-driven application, and that's usually
> frowned upon. At the same time if the application cannot function
> without those objects fully set up, then it has to complete the
> initialization whether it's in the constructor or elsewhere, so in that
> case it probably doesn't really matter... What other reasons are there?
> I can probably think of several. What if after some time you decide
> to introduce some other initialization, and it has to be done before
> your initial procedure? Do you create another global object and
> construct that (and put the new initialization there)? You can run into
> the problems connected to the order of things being initialized at
> different times on different runs. What if that initialization has to
> be conditional or have parameters that it reads from some other place?
> So, think of maintenance of your code, not just of writing it... There
> are probably other ones as well.
>
> V


I'll try and be more specific.

Basically my application (well it is actually a Unix daemon) has one
global configuration file in XML format. This lists a group of project
specific configuration files.

The global configuration file is read by one class and this then loads
all of the project specific configuration settings into a std::vector of
project specific classes.

The program then forks for each specific project configuration file and
uses those configurations for things such as which port to listen on and
so forth.

Basically the way the program works now by simply instantiating a new
global configuration object all the other objects are created
automatically and are populated with the correct data. No need for more
than a single statement (excluding try / catch etc).

Hopefully that gives you a better idea of what I am trying to achieve.
 
Reply With Quote
 
Chicken McNuggets
Guest
Posts: n/a
 
      01-11-2013
On 10/01/13 22:21, Greg Martin wrote:
> On 13-01-10 01:54 PM, Chicken McNuggets wrote:
>> I've seen various arguments against this primarily centring on the fact
>> that the only way to return errors in a constructor is to throw an
>> exception. Of course exceptions seem to be a somewhat controversial
>> subject in the C++ community so I'll try and avoid touching on that.
>>
>> But I have a couple of classes that are created when the program
>> launches and are freed just before the program terminates. It makes
>> sense for me to put a lot of logic in the constructor as these classes
>> are initialised from data in different configuration files which are
>> passed as command line arguments.
>>
>> Is there any reason I shouldn't put all the file loading / reading /
>> storing of data in the constructor? Or would you not consider this a
>> problem? For reference I'm using libxml2 for reading the data in the
>> configuration files.

>
> In cases where it makes sense I set error variables to be tested, just
> like a returned value, after the constructor completes.
>
> e.g.
> File *f = new File (path);
> if (!f->success ()) {
> std::cerr << f->error () << std::endl;
> // cleanup
> }


Yeah I considered this approach but it has a tendency to expose the
inner workings of the class if you are not careful, and I'd like to make
sure that re-writing the class in the future to take into account more
configuration options does not lead to a re-write of critical pieces of
the code since this class is of crucial importance to the rest of the
program.

 
Reply With Quote
 
Chicken McNuggets
Guest
Posts: n/a
 
      01-11-2013
On 11/01/13 01:17, Öö Tiib wrote:
>
> One issue that I have seen is when static object or static data
> member of class has complex constructor that may throw. If it throws
> then the program crashes when starting. Other issue that I have seen
> with long-running constructors is when the program later realizes that
> the object is not needed for anything aftrer all.


Thankfully the last point is never going to happen so that shouldn't be
a concern.

> Generally I tend to prefer complex constructors. For example when
> it is some "request" class and these have to be always sent somewhere
> anyway then I usually tend to merge its construction and sending both
> into constructor. That makes it simpler to use the class.


This is useful and is my primary reason for my current configuration. I
basically want the classes to handle themselves with very little outside
interaction required.

 
Reply With Quote
 
Stuart
Guest
Posts: n/a
 
      01-11-2013
On 01/11/13 Chicken McNuggets wrote:
[snip]
> Basically the way the program works now by simply instantiating a new
> global configuration object all the other objects are created
> automatically and are populated with the correct data. No need for more
> than a single statement (excluding try / catch etc).
>
> Hopefully that gives you a better idea of what I am trying to achieve.


It looks as if you put the whole logic into the constructor of a single
class. That seems a bit odd to me. Why do you need to create such an
object at all? Maybe you should use just a bunch of functions, it sounds
as if the problem domain does not really demand an object-oriented
approach. Ask yourself this: Will anybody use my class in a different
way in order to solve similar problems? If your answer is "No, this
class makes only sense in this particular project and I only need to
create a single instance of my class" than you can drop the whole class.

Regards,
Stuart

 
Reply With Quote
 
Greg Martin
Guest
Posts: n/a
 
      01-11-2013
On 13-01-11 04:42 AM, Chicken McNuggets wrote:
> On 10/01/13 22:21, Greg Martin wrote:
>> On 13-01-10 01:54 PM, Chicken McNuggets wrote:
>>> I've seen various arguments against this primarily centring on the fact
>>> that the only way to return errors in a constructor is to throw an
>>> exception. Of course exceptions seem to be a somewhat controversial
>>> subject in the C++ community so I'll try and avoid touching on that.
>>>
>>> But I have a couple of classes that are created when the program
>>> launches and are freed just before the program terminates. It makes
>>> sense for me to put a lot of logic in the constructor as these classes
>>> are initialised from data in different configuration files which are
>>> passed as command line arguments.
>>>
>>> Is there any reason I shouldn't put all the file loading / reading /
>>> storing of data in the constructor? Or would you not consider this a
>>> problem? For reference I'm using libxml2 for reading the data in the
>>> configuration files.

>>
>> In cases where it makes sense I set error variables to be tested, just
>> like a returned value, after the constructor completes.
>>
>> e.g.
>> File *f = new File (path);
>> if (!f->success ()) {
>> std::cerr << f->error () << std::endl;
>> // cleanup
>> }

>
> Yeah I considered this approach but it has a tendency to expose the
> inner workings of the class if you are not careful, and I'd like to make
> sure that re-writing the class in the future to take into account more
> configuration options does not lead to a re-write of critical pieces of
> the code since this class is of crucial importance to the rest of the
> program.
>


To me it's a question of the reasonable expectations of the programmer
using the class. In socket/file io experienced programmers know what is
under the hood of the class and expect to be able to test/catch errors.
My preference is for testing in those cases because of efficiency
concerns. Exposing the workings is a matter of what gets called "least
surprise". Alternatively, exposing open/read etc. with the usual return
values and allowing the programmer to test works - the programmer can
refer to system documentation.

For instance, with a non-blocking fd under unix you can provide a method
to test for EAGAIN and EAGAIN | EWOULDBLOCK or return the result of the
system call directly and let the programmer worry about it.

Here's an example from a non-blocking socket. It doesn't look much
different then if written without OO but the functionality is
encapsulated - still it should be clear, to someone who has used
non-blocking socket io in unix, what is happening. (the code is from an
accept loop using the epoll api - hence the break).

Socket insock = ss->accept ();

if (insock.error ()) {
if (insock.errorAgain () || insock.errorWouldBlock ()) {
break;
} else {
std::cerr << insock.errorMsg () << std::endl;
break;
}
}
insock.setNonBlocking ();





 
Reply With Quote
 
Öö Tiib
Guest
Posts: n/a
 
      01-11-2013
On Friday, 11 January 2013 14:45:21 UTC+2, Chicken McNuggets wrote:
> On 11/01/13 01:17, Öö Tiib wrote:
> >
> > One issue that I have seen is when static object or static data
> > member of class has complex constructor that may throw. If it throws
> > then the program crashes when starting. Other issue that I have seen
> > with long-running constructors is when the program later realizes that
> > the object is not needed for anything aftrer all.

>
> Thankfully the last point is never going to happen so that shouldn't be
> a concern.


Never say "never". It is never going to happen in optimal design. In
practice no design is optimal. I will try to bring some example of it
.... hmm.

Lets say (in light of previous example of mine) that there is number (x)
of complex objects that the software has to monitor with requests. Each
object has number (y) of complex parameters, setting all the space of
requesting parameters and ability to interpret the results of requests
up for monitoring will take some (x*y) time and space.

It may be that actually values of only few (z, z is smaller than y)
parameters are needed for routine monitoring. All (y) are needed only
on corner case for reasons when more attention toward particular object
is needed. Preparing less (x*z) parameters is certainly cheaper, but it
might be harder to develop such constructor than one that prepares all
right away.

The difference between costs (x*y-x*z) is usually not immediately
apparent. Often it is actually insignificant. Added complexity by
preparing subset may add other costs and lower the robustness of the design..
It can not be said what (if anything) in here "can be issue" or "hard to
notice" without profiling the whole thing.

That means effort. No human can deliver endless effort of weighting such
odds. On the contrary. Numerous people will name optimizations that give
insignificant improvement gained with measurable effort as "preliminary".
They mean preliminary in sense that applied without measuring. Preparing
the alternatives and measuring is however itself an effort. Therefore no
program is ever optimal.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Should rake remove t.name on failed tasks? Josef Wolf Ruby 1 11-29-2006 03:53 PM
perform dynamic tasks for C/C++/ObjC programs YL Python 0 02-02-2006 04:53 PM
Minimun permissions needed to perform Exchange related Tasks =?Utf-8?B?Z2JveWQ=?= MCSE 0 10-20-2005 03:16 PM
For expert on complex loops (reposted) - complex looping problem news.amnet.net.au Java 1 04-13-2004 07:10 AM
Re: = operator should automatically perform appropriate casts cgbusch Java 2 07-08-2003 03:58 PM



Advertisments