views:

222

answers:

5

I have a class with a static member:

class MyClass
{
public:
    static const SomeOtherClass myVariable;
};

Which I initialize in the CPP file like so:

const SomeOtherClass MyClass::myVariable(SomeFunction());

The problem is, SomeFunction() reads a value from the registry. If that registry key doesn't exist, it throws an exception. This causes my program to explode without giving the user any useful output... is there some way I can catch the exception so I can log it?

+5  A: 

Perhaps the best thing to do would be to add the registry key to a list instead of looking it up, then as soon as main() is entered, go through and look up all of the keys in the list. I don't want to be preachy but situations like this are exactly why it's generally a bad idea to do significant processing before main() is entered.

Dan Olson
the problem with this is that my object is a constant... it _must_ be initialized at that time. unless I use const_cast...
rmeador
True, I overlooked that. You could certainly make the global objects pointers and initialize them inside of main, but this could involve a lot of code changes. It's academic at this point... people have suggested a practical workaround in other answers and my only advice is for how to avoid the pre-main() behavior. However removing that behavior may not be necessary (though it could avoid other issues as well).
Dan Olson
@MSN, Sorry, I'm unsure what you mean... what does iostream have to do with the question or answer?
Dan Olson
@Dan, whups. I meant that for the answer that had std::cerr...
MSN
A: 

You can wrap the function inside another function that catches the exception and alerts the user of the problem (or creates the key with a safe default value)

David Rodríguez - dribeas
+4  A: 

Sure -- wrap SomeFunction() in a function like:

int static_error;

void SomeFunctionWrapper() { 
    try { 
        SomeFunction();
    }
    catch(...) { // something more specific if possible
        static_error = 1;
    }
}

Then upon entry to main, you'll want to check for static_error != 0 and print an appropriate error message if needed (unfortunately, you can't know if std::cerr exists yet in your exception handler, so if you want to print from there, you'll have to do something like C FILE*-based output).

Jerry Coffin
@Jerry, according to the C++ standard (whee, I love saying that) 27.4 subsection 2, static object constructors and destructors can access the iostream global objects.
MSN
@MSN: (actually §27.**3**/2). For some reason I'd never noticed that footnote before. Obviously the intent is there, but I don't see any normative language that really guarantees that. In any case, your point is probably right: the code I have above is being overly paranoid.
Jerry Coffin
A: 

You could make a wrapper class that delays construction of the object. Then when its first used, it will throw where ever its first used, if the constructor throws.

This has the benefit of not having alot of code run before main() is called, and if you don't ever actually use the global object it won't ever be initialized.

The code:

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread/once.hpp>
#include <iostream>

const boost::once_flag DEFAULT_ONCE_FLAG = BOOST_ONCE_INIT;
template <typename T>
class DelayedConstruction {
  public:
  DelayedConstruction(boost::function<T* (void) > const & init = &DelayedConstruction::default_initializer ) :
    m_initializer(init), m_flag(DEFAULT_ONCE_FLAG) { }

  T const & operator*() const { 
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this) ) ;
    if ( ! m_object )
      throw std::runtime_error("Object could not be initialized") ;
    return *m_object ;
  }
  T const * operator->() const {
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this) ) ;
    if ( ! m_object )
      throw std::runtime_error("Object could not be initialized") ;
    return m_object.get() ;
  }
  static T* default_initializer() { return new T; }
  private:
  void initialize() const {
    m_object.reset( m_initializer() ) ;
  }
  boost::function<T* (void) > m_initializer ;
  mutable boost::scoped_ptr<T> m_object ;
  mutable boost::once_flag m_flag ; 
};

struct Foo {
  Foo(int x = 0) : m_x(x) {
    if ( x == 1 ) throw std::runtime_error("Can't be 1") ;
  }
  int m_x ;
} ;

Foo* make_custom_foo() {
  return new Foo(1) ;
}

DelayedConstruction< const Foo > g_myFoo ;
DelayedConstruction< const Foo > g_anotherFoo(&::make_custom_foo) ;

int main() {

  try {
    std::cout << "My Foo: " << g_myFoo->m_x << std::endl ;
    std::cout << "Another Foo: " << g_anotherFoo->m_x << std::endl ;
  } catch ( std::runtime_error const & e ) {
    std::cout << "ERROR: " << e.what() << std::endl ;
  }

  return 0 ;
}

Prints out:

My Foo: 0
ERROR: Can't be 1
Eld
+2  A: 

I don't like static data members much, the problem of initialization being foremost.

Whenever I have to do significant processing, I cheat and use a local static instead:

class MyClass
{
public:
    static const SomeOtherClass& myVariable();
};

const SomeOtherClass& MyClass::myVariable()
{
  static const SomeOtherClass MyVariable(someOtherFunction());
  return MyVariable;
}

This way, the exception will be throw only on first use, and yet the object will be const.

This is quite a powerful idiom to delay execution. It had a little overhead (basically the compiler checks a flag each time it enters the method), but better worry about correctness first ;)

If this is called from multiple threads:

  • if your compiler handles it, fine
  • if your compiler does not, you may be able to use local thread storage (it's const anyway)
  • you could use boost::once in the Boost.Threads library
  • since it's const, you may not care if it's initialized multiple times, unless someOtherFunction does not support parallel execution (beware of resources)

Guideline: only use static or global variables instantiation for simple objects (that cannot throw), otherwise use local static variables to delay execution until you can catch the resulting exceptions.

Matthieu M.
just cause it const doesn't mean you don't care about it being initialized multiple times. It could be access a resource which you really only want to take the hit once for. SomeOtherFunction could also take a lot of time to run. Which case you probably don't want it to execute before main is ran anyway.
Eld
True, I just wanted to point out that it may not be necessary to worry about synchronization: `const` means that it will never change once initialized, whilst if it were not (think of a counter, for example), resetting after it's been used would screw things up. Of course, it's up to the OP to weight the risks at hand... I'll edit the answer to make this last point clearer.
Matthieu M.
I'm pretty sure my code doesn't have any threading issues with this... it should be initialized before any extra threads are started up. I have implemented this solution and it seems to solve the problem. Thanks!
rmeador