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.