2

I'm implementing code to exchange TCP messages and I came across an interesting approach to message construction in this stackoverflow posting. It uses a factory pattern in which the type of object to be created is registered with the factory. The registration function is a member template whose template parameter is the type of object to create but not an argument to the function itself (which is a corresponding integer identifier).

/**
* A class for creating objects, with the type of object created based on a key
* 
* @param K the key
* @param T the super class that all created classes derive from
*/
template<typename K, typename T>
class Factory { 
private:
    typedef T *(*CreateObjectFunc)();
    /**
    * A map keys (K) to functions (CreateObjectFunc)
    * When creating a new type, we simply call the function with the required key
    */
    std::map<K, CreateObjectFunc> mObjectCreator;

    /**
    * Pointers to this function are inserted into the map and called when creating objects
    *
    * @param S the type of class to create
    * @return a object with the type of S
    */
    template<typename S> 
    static T* createObject(){ 
        return new S(); 
    }

    //skip a bunch of code... 
public:
    /**
    * Registers a class to that it can be created via createObject()
    *
    * @param S the class to register, this must ve a subclass of T
    * @param id the id to associate with the class. This ID must be unique
    */ 
    template<typename S> 
    void registerClass(K id){ 
      if (mObjectCreator.find(id) != mObjectCreator.end()){ 
        //your error handling here
      }
      /*Note: I removed the explicit template parameters from the
       *call to std::make_pair() in the original code referenced in
       *text above since it caused complilation errors. */
      mObjectCreator.insert( std::make_pair(id, &createObject<S> ) ); 
    }
 }

I don't see how the type to be created () is registered since it's type is not an argument to the registration function and therefore cannot be deduced from those arguments. Is this a problem in the code or do I need to declare that function in some way?

Rich Ramos
  • 141
  • 1
  • 10
  • 1
    `&createObject` causes `createObject` to be instantiated. For each type `S` passed as a template parameter of `registerClass`, a new function `createObject` is stamped out from template definition, and the address of this function stored, to be called later. The type is in effect embedded in the body of the corresponding instance of `createObject` function. This technique is known as "type erasure" (you can access something that knows about type `S` via something - here, a function pointer - that doesn't mention type `S` in any way; the type has been "erased"). – Igor Tandetnik Jul 08 '18 at 14:40
  • @IgorTandetnik : I get that but my question is : How do I pass type `S` into the template function if not via a function argument? – Rich Ramos Jul 08 '18 at 14:48
  • 1
    As a template argument, of course. That's why it takes a template parameter. As in `factory.registerClass(myId);` – Igor Tandetnik Jul 08 '18 at 14:49
  • @VTT - Fixed. I guess my attempt to reduce verbosity just reduced clarity. – Rich Ramos Jul 08 '18 at 15:09
  • The class template seems to be a red herring. You are asking, in essence, how to call a function template and pass it a type template parameter explicitly. Yet you have already done it yourself (though redundantly) in your own code: `std::make_pair(...)` is just such a call. – n. m. could be an AI Jul 08 '18 at 15:29
  • 1
    One additional catch here: if you want to call `registerClass` from another template where the `factory` object has a dependent type, you'll need the syntax `factory.template registerClass(myId);` – aschepler Jul 08 '18 at 15:31
  • The factory.registerClass(myId) offered by @IgorTandetnik moved this forward. But I'm getting a compilation error : template argument deduction failed (in the call to std::make_pair<>()). Not sure what's going on there. – Rich Ramos Jul 08 '18 at 16:21
  • Updating a previous comment: The factory.registerClass(myId) offered by @IgorTandetnik moved this forward. But I'm getting a compilation error : no matching function for call to 'make_pair(int&, )'. And later: error template argument deduction failed (in the call to std::make_pair<>()). Not sure what's going on there. – Rich Ramos Jul 08 '18 at 16:31
  • Make it `mObjectCreator.insert( std::make_pair(id, &createObject ) );` - drop explicit template arguments for `make_pair`; they are wrong. With that, [your code compiles](http://rextester.com/EUBBL19852) – Igor Tandetnik Jul 08 '18 at 16:37
  • @IgorTandetnik - That worked. Thank you very much. – Rich Ramos Jul 08 '18 at 17:22

1 Answers1

0

The answer to my own question was provided by @IgorTandetnik in the comments.

The solution is to simply pass the template parameter into the template function by using the normal brackets between the template function name and the paranthesized arguments as:

factory.registerClass<MyClass>(myId);
Rich Ramos
  • 141
  • 1
  • 10