views:

568

answers:

9

Hi,

This seems to be a trivial question but I got hung on it for a few hours now (maybe too much Java killed my C++ braincells).

I have created a class that has the following constructor (i.e. no default constructor)

VACaptureSource::VACaptureSource( std::string inputType, std::string inputLocation ) {
    if( type == "" || location == "" ) {
 throw std::invalid_argument("Empty type or location in VACaptureSource()");
}
type = inputType;
location = inputLocation;

// Open the given media source using the appropriate OpenCV function.
if( type.compare("image") ) {
 frame = cvLoadImage( location.c_str() );
 if( !frame ) {
  throw std::runtime_error("error opening file");
 }
}
else {
 throw std::invalid_argument("Unknown input type in VACaptureSource()");
}

}

When I want to create an instance, I use

    // Create input data object
try {
 VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
catch( invalid_argument& ia ) {
 cerr << "FD Error: " << ia.what() << endl;
 usage(argv[0]);
}
catch( runtime_error& re ) {
 cerr << "FD Error: " << re.what() << endl;
 usage(argv[0]);
}

However, in this case the instance is local to this block and I can't refer to it anywhere else. On the other hand, I can't say

VACAptureSource input;

at the beginning of the program since there's no default constructor.

What is the correct way to do this?

Thanks!

+4  A: 

A local variable is scoped to the block in which it's allocated (like Java) but it'll destruct as soon as the block ends (unlike Java) so you should either do all the stuff you want in the try block itself (which might not be desirable if you want to only handle constructor exceptions) or you should allocate the object somewhere else (e.g. heap) and use a pointer in the parent block to access it.

Mehrdad Afshari
+7  A: 

What about using a pointer (or some RAII version thereof)?

VACaptureSource* input = NULL;

try {
    input = new VACaptureSource(...);
} catch(...) {
    //error handling
}

//And, of course, at the end of the program
delete input;
CAdaker
Thanks, I thought about about that, but thought that pointers "evil" in C++, i.e. used only as a last resort? Is there no other way than using pointers?
recipriversexclusion
"Evil" is a pretty strong word. However, as suggested by CAdaker, a RAII (i.e. auto-destructing) variant, such as std::auto_ptr - or any appropriate Boost auto pointer - would be a sensible option.
RaphaelSP
By the way, you probably only want to use valid instances, which means that you may want to re-throw from with the catch blocks after having done what needs to be done.
RaphaelSP
That seems silly. Why would you want to use a pointer when a normal variable would work!!!!
Martin York
@Martin: Why would you not use a pointer when an auto variable does not work? (For once I am disagreing with you). See a longer comment in your answer.
David Rodríguez - dribeas
+2  A: 

You may use a pointer

VACaptureSource* input;
// Create input data object
try {
    input = new VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
catch( invalid_argument& ia ) {
    cerr << "FD Error: " << ia.what() << endl;
    usage(argv[0]);
}
catch( runtime_error& re ) {
    cerr << "FD Error: " << re.what() << endl;
    usage(argv[0]);
}

And you need to free the object when you finish to use it

delete input
ThibThib
Again I do not see the nedd for a pointer.
Martin York
+11  A: 

why do you need to refer to it outside the try block?

Instead of

try {
  VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
//catch....

//do stuff with input

you could move everything into the try block:

try {
  VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
  //do stuff with input
}
//catch....

or you could factor it out into a separate function, which is called from the try block:

void doStuff(VACaptureSource& input){
  //do stuff with input
}

try {
  VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
  doStuff(input);
}
//catch....

The last one even gives you the nice bonus of separating the construction from use, which places nicely into unit tests where you might want the function to work on a mock object instead.

jalf
Another good answer by you with several alternatives included. i like these :) But to be fair and give possible reason, i also often wanted that stuff in a "try" block "leaked out" into the enclosing scope. But this could be dangerous, consider this code: try { string a; /* <- assume that throws */ string b; } catch(...) { } b.size(); /* oops, b not yet constructed! */
Johannes Schaub - litb
Yep, I often want to do the same. Bit since there's no obvious way to do that, we have to settle for the workarounds.
jalf
Also there is the possibility of wanting to catch the std::invalid_argument from the constructor but leave all exceptions propagate from the rest of the code. Consider a method to obtain a value that may throw, but that in this specific piece of code you have a default value in case the operation fails. Later you want to use that value with another method that can also throw the same exception, but you have no sensible way of continuing your flow in that case. You want to catch the first but not the later exceptions.
David Rodríguez - dribeas
A: 

What about adding a default constructor which leaves the object in a special unconfigured state? Then have an create() function for actually creating it.

Then you can do:

VACaptureSource input;
try
{
   input.create("image", "...");
}
catch(...)
{
   ...
}

Depending on the situation this might be better than messing with pointers. Although then you also have to check if the create() was actually called before doing something...

rve
Thats very Java like. In C++ you try and put the object into a usable state in the constructor. Doing it this way you need to maintain state information about weather the object has been created and this state information will filter through to all the other methods. This makes the code uglier (smellier).
Martin York
Don't. Objects are either constructed or not constructed. There is no in-between. A throwing constructor means that the object is not usable in that state and thus the sensible thing to do is not having such an object alive. If you do you will start adding code flows with apparently correct objects that are not that you will have to verify and test, and there will be many more places where you can make mistakes, and at one point, no matter how good you are you will make one mistake, it will happen far away from the point of creation and you will have a nice late-debug night.
David Rodríguez - dribeas
@Martin York: No, it is not very Java like. Java-like is defining a pointer initializing to null/NULL/0 and then allocating in the heap with new. Java is not a pointer-less language, only a pointer-arithmetic-less language, where pointers (by the name of reference) crawl up in every line of code.
David Rodríguez - dribeas
@dribeas: Having objects which are not really usable after construction are not that uncommon. Like std::ofstream for example can work this way by calling open() after construction.I do think the object should be somewhat usable after construction and the code using the object should not be filled with checks whether the object is ok or not. I think it depends on the object if this is usable or not.
rve
+1  A: 

I don't actually see any probelms here:

A couple of things I would update:

  • Catch exceptions by const reference.
  • The compiler may optimize away the copy construction in your code
    But it looks neater without it. Just declare input with its parameters.
  • I would refactor the constructor to take const reference parameters
    And I would initialise them in the initialiser list.
  • Also I would make sure the member 'frame' is actually a smart pointer.

So I would do this (for clarity).

VACaptureSource::VACaptureSource( std::string const& inputType,
                                  std::string const& inputLocation )
      :type(inputType)
      ,location(inputLocation)
{
    // Other Code that throws.
}
void playWithCode()
{
    // Get input information from user.
    VACaptureSource input("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");

    // use the input object.
    // Do not need to play with pointers here.
}
int main()
{
    try
    {
        playWithCode();
    }
    catch( invalid_argument const& ia )
    {    cerr << "FD Error: " << ia.what() << endl;
         usage(argv[0]);
    }
    catch( runtime_error const& re )
    {    cerr << "FD Error: " << re.what() << endl;
         usage(argv[0]);
    }
}
Martin York
The problem here is that all invalid_argument exceptions in playWithCode are caught at the same time with the same semantics. It might be the case where in the 'user the input object' another operation can throw a invalid argument that you want to process differently.
David Rodríguez - dribeas
Sorry, but I can not make heads nor tails of that.
Martin York
Say you want to create a VACaptureSource, but you can resort to another CaptureSource if that fails and later you want to use it in a call that can throw std::runtime_error, but you cannot recover from a failure there. You would want to try creating VACaptureSource, catch exceptions and continue, but let std::runtime_error propagate up the stack if it is thrown later on the 'playWithCode()' method.
David Rodríguez - dribeas
+3  A: 

Can I just observe that just about any but the most trivial constructor can be expected to throw an exception. You should therefore not consider exceptions to be "special" in some sense, but instead write your code so that it deals with them naturally. This means using RAII, and the other techniques that other answers here have suggested.

anon
Not an answer, but a good point to reason about in the general exception handling problem.
David Rodríguez - dribeas
A: 

Simple. Don't throw exceptions within a constructor. Not only do you have to wrap the constructor in the try block, you won't be able to handle memory very well in the event you catch an exception (do you call the destructor? how much of the class's memory needs to be deleted?)

UPDATE0: Although, I'm not sure if memory management is a problem if you're using an instance.

UPDATE1: Hmmm, maybe I'm thinking about exceptions in destructors.

int
main2(int argc, char* argv[])
{
  MyClass class;
  class.doSomething();
}

int
main(int argc, char* argv[])
{
  int result = 0;
    try {
      main2(argc, argv);
    } catch (std::exception& se) {
      // oh noes!
       result = 1;
    }
  return result;
}
Terry Lorber
C++ has a rule about throwing exceptions from a constructor: any sub objects created before the exception are properly destroyed, but the object whose constructor failed was never created and is not destroyed. And all memory allocated for the object is deallocated.
Max Lybbert
@Max Lybbert - Thanks. I've applied the "never throw exceptions in destructors" rule to constructors before, maybe I'll remember now.
Terry Lorber
+2  A: 

I can't say

VACaptureSource input;

at the beginning of the program since there's no default constructor.

There is a good reason you didn't create a default constructor: namely that a VACaptureSource only makes sense when associated with a file. So don't create a default constructor. Instead simply recognize that the scope of the VACaptureSource object is the try block, and use it inside there.

Max Lybbert