Skip to main content
Better variable names for the example without a registry.
Source Link
G. Sliepen
  • 69.3k
  • 3
  • 75
  • 180
template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerInstance;handlerGenerator;
Generator<JsonParser>      jsonParserInstance;jsonParserGenerator;
Generator<NOSQLOperations> noSQLOperationsInstance;noSQLOperationsGenerator;
Generator<DBOperations>    dbOperationsInstance;dbOperationsGenerator;

handlerInstancehandlerGenerator =         [&] { return std::make_unique<Handler>(jsonParserInstancejsonParserGenerator()); };
jsonParserInstancejsonParserGenerator =      [&] { return std::make_unique<JsonParser>(noSQLOperationsInstancenoSQLOperationsGenerator()); };
noSQLOperationsInstancenoSQLOperationsGenerator = [&] { return std::make_unique<NOSQLOperations>(dbOperationsInstancedbOperationsGenerator()); };
dbOperationsInstancedbOperationsGenerator =    [&] { return std::make_unique<DBOperations>(); }; 

auto handler = handlerGenerator();
std::cout << handlerInstance()handler->handle() << '\n';
template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerInstance;
Generator<JsonParser>      jsonParserInstance;
Generator<NOSQLOperations> noSQLOperationsInstance;
Generator<DBOperations>    dbOperationsInstance;

handlerInstance =         [&] { return std::make_unique<Handler>(jsonParserInstance()); };
jsonParserInstance =      [&] { return std::make_unique<JsonParser>(noSQLOperationsInstance()); };
noSQLOperationsInstance = [&] { return std::make_unique<NOSQLOperations>(dbOperationsInstance()); };
dbOperationsInstance =    [&] { return std::make_unique<DBOperations>(); };

std::cout << handlerInstance()->handle() << '\n';
template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerGenerator;
Generator<JsonParser>      jsonParserGenerator;
Generator<NOSQLOperations> noSQLOperationsGenerator;
Generator<DBOperations>    dbOperationsGenerator;

handlerGenerator =         [&] { return std::make_unique<Handler>(jsonParserGenerator()); };
jsonParserGenerator =      [&] { return std::make_unique<JsonParser>(noSQLOperationsGenerator()); };
noSQLOperationsGenerator = [&] { return std::make_unique<NOSQLOperations>(dbOperationsGenerator()); };
dbOperationsGenerator =    [&] { return std::make_unique<DBOperations>(); }; 

auto handler = handlerGenerator();
std::cout << handler->handle() << '\n';
added 71 characters in body
Source Link
G. Sliepen
  • 69.3k
  • 3
  • 75
  • 180

No. Template parameter packsParameter packs can bind to zero or more template arguments, so you don't need to make a special case for it. You can just write:

template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerInstance;
Generator<JsonParser>      jsonParserInstance;
Generator<NOSQLOperations> noSQLOperationsInstance;
Generator<DBOperations>    dbOperationsInstance;

handlerInstance =         [][&] { return std::make_unique<Handler>(jsonParserInstance()); };
jsonParserInstance =      [][&] { return std::make_unique<JsonParser>(noSQLOperationsInstance()); };
noSQLOperationsInstance = [][&] { return std::make_unique<NOSQLOperations>(dbOperationsInstance()); };
dbOperationsInstance =    [][&] { return std::make_unique<DBOperations>(); };

std::cout << handlerInstance()->handle() << '\n';

If you want to declare the instances in a header file that is included in multiple source files, you can make them inline.

Note that now you can easily declare two different generators of the same type.

No. Template parameter packs can bind to zero or more template arguments, so you don't need to make a special case for it. You can just write:

template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerInstance;
Generator<JsonParser>      jsonParserInstance;
Generator<NOSQLOperations> noSQLOperationsInstance;
Generator<DBOperations>    dbOperationsInstance;

handlerInstance =         [] { return std::make_unique<Handler>(jsonParserInstance()); };
jsonParserInstance =      [] { return std::make_unique<JsonParser>(noSQLOperationsInstance()); };
noSQLOperationsInstance = [] { return std::make_unique<NOSQLOperations>(dbOperationsInstance()); };
dbOperationsInstance =    [] { return std::make_unique<DBOperations>(); };

std::cout << handlerInstance()->handle() << '\n';

If you want to declare the instances in a header file that is included in multiple source files, you can make them inline.

No. Parameter packs can bind to zero or more arguments, so you don't need to make a special case for it. You can just write:

template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerInstance;
Generator<JsonParser>      jsonParserInstance;
Generator<NOSQLOperations> noSQLOperationsInstance;
Generator<DBOperations>    dbOperationsInstance;

handlerInstance =         [&] { return std::make_unique<Handler>(jsonParserInstance()); };
jsonParserInstance =      [&] { return std::make_unique<JsonParser>(noSQLOperationsInstance()); };
noSQLOperationsInstance = [&] { return std::make_unique<NOSQLOperations>(dbOperationsInstance()); };
dbOperationsInstance =    [&] { return std::make_unique<DBOperations>(); };

std::cout << handlerInstance()->handle() << '\n';

If you want to declare the instances in a header file that is included in multiple source files, you can make them inline.

Note that now you can easily declare two different generators of the same type.

Source Link
G. Sliepen
  • 69.3k
  • 3
  • 75
  • 180

Naming things

What are NP and PRS abbreviations of? I don't have a clue. Especially for concepts I would choose clear names that express what the concepts mean.

Factory is a very generic name. A factory of what? What if you have a code base which needs more than one factory type? I would choose a more distinctive name for this class, even if it is never visible to any other source files.

It's not thread safe

It is meant to be for a multithreaded application [...]

I don't see any mutexes or atomic operations in your code. Note that std::unique_ptr does not have anything to do with thread-safety.

No need for two overloads

Do I need 2 overloaded functions of registerInstance()?

No. Template parameter packs can bind to zero or more template arguments, so you don't need to make a special case for it. You can just write:

template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

template <typename T>
static void registerInstance(Generator<T> gen)
{
    getInstance().factoryMap_[typeid(T)] = gen;
}

What about multiple instances of the same type?

What if you have some complicated scenario where you have two different databases? Maybe you have two different classes derived from DBOperations, or maybe just the same class but you have to pass a different database URI to the constructor? Consider:

registerInstance<DBOperations>([] {
    return std::make_unique<MongoDBOperations>("example.com/db1");
});
registerInstance<DBOperations>([] {
    return std::make_unique<CouchDBOperations>("example.com/db2");
});

This is a problem, because there is only place for one DBOperations in your factoryMap_. Also problematic is that the second call to registerInstance() will silently overwrite the previous one.

Do you need a registry at all?

The problem with your registry is that it is indexed based on types. Since types need to be known at compile time, there is not much point in having a run time map. The only thing you want is to be able to write code that uses a given object before it is defined. But you can declare variables before assigning values, so you can do:

template <typename T, typename... Ps>
using Generator = std::function<std::unique_ptr<T>(Ps &&...arg)>;

Generator<Handler>         handlerInstance;
Generator<JsonParser>      jsonParserInstance;
Generator<NOSQLOperations> noSQLOperationsInstance;
Generator<DBOperations>    dbOperationsInstance;

handlerInstance =         [] { return std::make_unique<Handler>(jsonParserInstance()); };
jsonParserInstance =      [] { return std::make_unique<JsonParser>(noSQLOperationsInstance()); };
noSQLOperationsInstance = [] { return std::make_unique<NOSQLOperations>(dbOperationsInstance()); };
dbOperationsInstance =    [] { return std::make_unique<DBOperations>(); };

std::cout << handlerInstance()->handle() << '\n';

If you want to declare the instances in a header file that is included in multiple source files, you can make them inline.