0

I need to use different implementations of some of the methods in my class, therefore I use a couple of boost::function variables to point to correct method. everything seems working except this variable:

boost::function<void(void)> onExit

which is called in the destructor. Have a look at my simple methods please:

//constructor
Logger::Logger(std::string id_){

    /*...other stuff...*/
    //decision point to use which implementation
    initDef();
}

//Default Implementation initializer
void Logger::initDef()
{
    /*...other stuff...*/
    onExit = boost::bind(&Logger::onExitDef,this);
    if(onExit.empty()){
        std::cout << "onExit is Empty in initialization" << std::endl;
    }
}
//destructor
Logger::~Logger(){
    if(onExit.empty()){
        std::cout << "onExit is Empty in destructor" << std::endl;
    }
    onExit();//error in here
}

a breakpoint inside initDef() shows the following after initializing onExit. As you can see the vtable is not empty :

    Details:{<boost::function0<void>> = {<boost::function_base> = {vtable = 0x1469681 <void boost::function0<void>::assign_to<boost::_bi::bind_t<void, boost::_mfi::mf0<void, Logger>, boost::_bi::list1<boost::_bi::value<Logger*> > > >(boost::_bi::bind_t<void, boost::_mfi::mf0<void, Logger>,
 boost::_bi::list1<boost::_bi::value<Logger*> > >)::stored_vtable+1>, functor = {obj_ptr = 0xd32806 <Logger::onExitDef()>, type = {type = 0xd32806 <Logger::onExitDef()>, const_qualified = false, volatile_qualified = false}, func_ptr = 0xd32806 <Logger::onExitDef()>, bound_memfunc_ptr = {memfunc_ptr = (void (boost::detail::function::X::*)(boost::detail::function::X * const, int)) 0xd32806 <Logger::onExitDef()>, obj_ptr = 0x45903200}, 
obj_ref = {obj_ptr = 0xd32806 <Logger::onExitDef()>, is_const_qualified = false, is_volatile_qualified = false}, data = 6 '\006'}}, static args = <optimized out>, static arity = <optimized out>}, <No data fields>}

but in the destructor, onExit.empty() holds true(vtable is empty/null) :

    Details:{<boost::function0<void>> = {<boost::function_base> = {vtable = 0x0, functor = {obj_ptr = 0x0, type = {type = 0x0, const_qualified = false, volatile_qualified = false},
 func_ptr = 0x0, bound_memfunc_ptr = {memfunc_ptr = NULL, obj_ptr = 0x0}, obj_ref = {obj_ptr = 0x0, is_const_qualified = false, is_volatile_qualified = false}, data = 0 '\0'}}, static args = <optimized out>, static arity = <optimized out>}, <No data fields>}

and naturally, the obvious error is:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_function_call> >'
  what():  call to empty boost::function

I will appreciate you you share your thoughts with me: how does this happen and how do you think I can solve this issue?

thanks

rahman
  • 4,820
  • 16
  • 52
  • 86
  • onExitDef isn't virtual is it? Not that this would be a problem in itself, I'm just curious. – jhoffman0x Aug 05 '14 at 02:12
  • Are you sure that there isn't anything in `initDef` that could possibly shadow `onExit`? – T.C. Aug 05 '14 at 02:16
  • @T.C. `initDef` initializes `onExit` . Other than that...no, I dont think so. – rahman Aug 05 '14 at 02:42
  • @rahman What do you see if you set a breakpoint at the closing brace of the constructor and look at the contents of `onExit`? – T.C. Aug 05 '14 at 03:34

1 Answers1

1

First of all a short self contained correct compatible example would have been great.

I have written a sample program assuming lot of code to emulate the scenario above and this is what I have observed:

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>


struct Logger{

  Logger(){}
  Logger(std::string id_);

  ~Logger();

  void initDef();

  void onExitDef();


  boost::function< void(void) > onExit;
};

//constructor
Logger::Logger(std::string id_){

  /*...other stuff...*/
  //decision point to use which implementation
  initDef();
}

//Default Implementation initializer
void Logger::initDef(){

  /*...other stuff...*/
  onExit = boost::bind(&Logger::onExitDef,this);
  if(onExit.empty()){
    std::cout << "onExit is Empty in initialization" << std::endl;
  }
}
//destructor
Logger::~Logger(){

  if(onExit.empty()){
    std::cout << "onExit is Empty in destructor" << std::endl;
  }
  onExit();//error in here
}

 void Logger::onExitDef(){

    std::cout << "onExitDef" << std::endl;
}


int main(){


  Logger obj1; // this object creation does not initialize onExit
  Logger obj2("hi"); //this object creation ensures onExit is initialized and destructor gets a valid onExit

//since initDef is not explicitly called or indirectly called from inside of constructor, onExit is not initialized and hence exception received when destructor of obj1 is called
//  obj1.initDef(); 

  return 0;
}

Running the above code gives same result as mentioned in question. The problem here is that in main() we are create 'obj1' which invokes default constructor. From default constructor initDef is not called and hence onExit is not initialized to some valid func pointer. Therefore when object 'obj1' is destructed exception is received. To avoid the exception one needs to explicitly call initDef using obj1.

There is no issue in using obj2.

Output of above code is:

onExitDef
onExit is Empty in destructor
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_function_call> >'
  what():  call to empty boost::function
Aborted
Nik
  • 1,294
  • 10
  • 16
  • that is just great. I did create an empty constructor. I over sighted it when I got lost in different implementations and different types of object creation. thanks – rahman Aug 06 '14 at 00:08