Mafro34
Mafro34

Reputation: 520

Template function to return different types

Is it possible to have a method return values of different types with the type determined at run time?

I am writing something that will encode and decode using different ciphers. Each cipher has a key of a different type and at some stage I need to allow for the cipher to fetch this key.

To avoid writing three (there are three ciphers for now) separate methods to return keys of different types and as only one cipher will be used at one time I am trying to do the following:

in parser.h:

    template <typename T>
    T get_key(void) const;

in parser.cpp:

/**
 * The key to be used for encoding or decoding.
 * 
 * @return      The key to be used.
 */
template <typename T>
T cmdline_parser::get_key(void) const
{
    if (vm.count("xor") > 0)// if xor cipher in use
        return vm["xor"].as<long>();// returns a key of type long
    else if (vm.count("caesar") > 0)// if caesar cipher in use
        return vm["caesar"].as<int>();// returns a key of type int
    else// vignere cipher in use
        return vm["vignere"].as<std::string>();// returns a key of type std::string

}

How I am trying to use it:

Crypt<VignereCipher, std::string, Group, Pack> c(parser.get_key());

Compiling with clang++ gives:

main.cpp:42:61: error: no matching member function for call to 'get_key'
    Crypt<VignereCipher, std::string, Group, Pack> c(parser.get_key());
                                                 ~~~~~~~^~~~~~~

and

./cmdline_parser.h:40:7: note: candidate template ignored: couldn't infer template         argument 'T'
T get_key(void) const;
  ^

Upvotes: 1

Views: 1024

Answers (1)

TemplateRex
TemplateRex

Reputation: 70516

There are 3 approaches that you could use all of which were mentioned in the comments. Here are some more details and tradeoffs for choosing between them:

  1. Factory Method. As @KerrekSB mentioned in the comments, you have to define an abstract base class and subclass each key type from. The factory function's return type is a pointer (preferably a smart pointer to deal with resource management) to AbstractKey. The factory's implementation however is a if-else ladder, switch statement or fancy-pancy table lookup based on the runtime value you are parsing. This works because of covariant return types. For a worked example see e.g. this site on design patterns. The main drawbacks are a lot of boiler-plate that you have to write (e.g. registring new types to the factory etc.), builtin-types are excluded, and your returned objects do not have value semantics.

  2. Boost.Variant. This was mentioned by @AnatolyS in the comments. This approach wraps a finite number of unrelated types inside a union and lets you use only one of those types at the time without dynamic allocation overhead. See the Boost documentation for how to use it. The main drawback is that it is not very extensible, and it takes quite a bit of trickery to dynamically select the key type you want.

  3. Boost.Any. This generalizes both solutions to have an arbitrary number of unrelated types wrapped inside a holder object. The main advantages are that you have value semantics, and no manual memory management (this is done behind the scene, though). The only practical drawback is that it requires Boost, which is not possible in some restrictive corporate settings.

Upvotes: 1

Related Questions