Derek
Derek

Reputation: 1125

How to use boost::call_once() on a function with arguments

I want to use boost::call_once() to achieve a thread-safe lazy-construction singleton scenario, however, the base singleton class has many derived classes thus the getInstance() function takes an argument to determine which derived class to initialize. The code looks like,

Singleton * Singleton::getInstance(Input * a) {
if (!instance) {
    instance = buildme(a);  //buildme() will return a derived class type based on input a.
    }
return instance;
}

I want to use boost::call_once(), but looks like it can only be used on functions with no arguments void (*func)(). If anybody knows about an alternative solution here please help.

Thanks.

EDIT::

Another question, how to call a non-static member function using call_once? I have a non-static init() member function of this class, but I couldn't find a correct syntax for calling it using boost::call_once(). Or should I make init() and everything used in it static?

Thanks.

Upvotes: 2

Views: 6208

Answers (2)

Chad
Chad

Reputation: 19022

You can bind additional function parameters to a functor object using boost::bind. Like this:

Input* input = ???;
boost::call_once(flag, boost::bind(&Singleton::getInstance, input));

You can use boost::bind to call non-static member functions as well, by passing the instance of the class on which you want to call the function to boost::bind.

class Foo
{
public:
   void func(int) { /* do something */}
};

Foo f;
boost::call_once(flag, boost::bind(&foo::func, &f, 10));

With C++11, you can use std::bind, here's another example. boost::bind is quite similar.

#include <utility>
#include <functional>
#include <iostream>
#include <string>

void f(int x)
{
   std::cout << "f(" << x << ")\n";
}

void g(int x, const std::string& y)
{
   std::cout << "g(" << x << ", " << y << ")\n";
}


int main()
{
   auto ten = std::bind(&f, 10);
   auto example = std::bind(&g, 20, "Twenty");

   ten();
   example();

   return 0;
}

Upvotes: 7

John5342
John5342

Reputation: 1149

C++11 contains an implementation of call_once (inspired by the equivalent Boost.Threads facility). It uses variadic templates and perfect forwarding to take an arbitrary number of arguments.

#include <mutex>
#include <string>

void only_called_once(int i, std::string const & str) {
  // We only get here once.                                                                                                                                                         
}

void call_free() {
  static std::once_flag once;
  std::call_once(once, only_called_once, 42, "The answer");
}

You can pass an arbitrary number of arguments after the callable and they will all be perfectly forwarded (including r-value/l-value, const, volatile, etc).

This also works for member functions. You just have to pass a pointer to an object (convertible to the type the member function belongs to) as the first argument after the callable.

struct bar {
public:
  void only_call_once(int i, std::string const & str);
};

void call_member() {
  static std::once_flag once;
  bar instance;
  std::call_once(once, &bar::only_call_once, &instance, 42, "The answer");
}

If you are stuck with Boost then you can use boost::bind for the same purpose as has already been explained in another answer. Member functions with boost::bind work the same way as above by passing a member function pointer and an instance as the following parameter.

Upvotes: 8

Related Questions