Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Tutorial 2 - Basic Factories

The easiest method for creating plugins in C++ is to use factories. Consider the following base class:

class animal {
public:
  animal(int age) : age_(age) {
  }
  virtual ~animal(void) {
  }
  virtual std::string get_name(void) {
    return "A generic animal";
  }
  int get_age(void) {
    return age_;
  }
protected:
  int age_;
};

The goal here is to create a main executable which creates and uses instances of the animal class. A shared library will then be created which includes classes derived from the animal class, which the main executable will also be able to load and use as animals.

The main executable will use a number of Extension and other Boost headers:

#include <iostream>
#include <map>

#include <boost/extension/factory.hpp>
#include <boost/extension/shared_library.hpp>
#include <boost/extension/type_map.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>

It will also use the animal header, which will also be used by the shared library:

#include "animal.hpp"

The main function is similar to that from Tutorial 1.

int main() {
  using namespace boost::extensions;
  std::string library_path = "libtutorial_2.extension";

  // Create shared_library object with the relative or absolute
  // path to the shared library.
  shared_library lib(library_path);

  // Attempt to open the shared library.
  if (!lib.open()) {
    std::cerr << "Library failed to open: " << library_path << std::endl;
    return 1;
  }

Here though, instead of using the shared_library::get function, we use the _shared_librarycall function. It expects a function with the following signature:

extern "C" void boost_extension_exported_type_map_function
  (boost::extensions::type_map& types);

A type_map is a generic container with items indexed by type - only one item of each type can be contained in it. To retrieve (or add) an integer to it, do the following:

int& my_int(my_type_map.get());

For the rationale of the type_map class, see the discussion on type safety.

// Use the shared_library::call to automatically call an
// Extension-specific function in the shared library,
// which takes a type_map.
type_map types;
if (!lib.call(types)) {
  std::cerr << "Function not found!" << std::endl;
  return 1;
}

Now, a std::map of factories, indexed by std::string is retrieved from the type_map that was sent to the shared library.

The factory is declared as factory<animal, int> meaning it returns pointers to instances of the animal class, and that it is used for constructors taking integer arguments.

// Retrieve a map of all animal factories taking an int and indexed
// by a string from the type_map.
std::map<std::string, factory<animal, int> >& factories(types.get());
if (factories.empty()) {
  std::cerr << "Animals not found!" << std::endl;
  return 1;
}

Now, iterate through the factory, creating each available animal.

  // Create each animal.
  int age = 1;
  for (std::map<std::string, factory<animal, int> >::iterator it
         = factories.begin();
       it != factories.end(); ++it) {
    ++age;
    std::cout << "Creating an animal using factory: " << it->first << std::endl;
    boost::scoped_ptr<animal> current_animal(it->second.create(age));
    std::cout << "Created an animal: " << current_animal->get_name()
              << " Age: " << current_animal->get_age() << std::endl;
  }
}

Now, the shared library will define a number of animals. It will use the following headers:

#include <iostream>
#include <map>

#include <boost/extension/extension.hpp>
#include <boost/extension/factory.hpp>
#include <boost/extension/type_map.hpp>

#include "animal.hpp"

The following animals will be exported:

class puma : public animal {
 public:
  puma(int age) : animal(age) {}
  virtual std::string get_name() {
    return "puma";
  }
};

class leopard : public animal {
 public:
  leopard(int age) : animal(age) {}
  virtual std::string get_name() {
    return "leopard";
  }
};

class wildcat : public animal {
 public:
  wildcat(int age) : animal(age) {}
  virtual std::string get_name() {
    return "wildcat";
  }
};

class cougar : public animal {
 public:
  cougar(int age) : animal(age) {}
  virtual std::string get_name() {
    return "cougar";
  }
};

The BOOST_EXTENSION_TYPE_MAP_FUNCTION declaration can be used in place of a function declaration to automatically insert the function definition referred to above, that takes a type_map. See <boost/extension/extension.hpp> for more details.

BOOST_EXTENSION_TYPE_MAP_FUNCTION {
  using namespace boost::extensions;
  std::map<std::string, factory<animal, int> >&
    animal_factories(types.get());
  animal_factories["Puma factory"].set<puma>();
  animal_factories["Leopard factory"].set<leopard>();
  animal_factories["Wildcat factory"].set<wildcat>();
  animal_factories["Cougar factory"].set<cougar>();
}

Now, compile the shared library and main executable. Then run the main executable in the directory with the compiled library. If all is successful, a message will be printed out for each animal.


PrevUpHomeNext