Search code examples
c++templatesmetaprogramminggetter-setter

Template setter setting a value of an unknown type. can I have a non template getter for this value?


I am writing a library for Command line arguments in c++. When a user declares a value they expect to receive from the command line, I want to return this value in a type they specify. For instance a command line program for adding multiple integers. The user would want to receive the numbers as int and so they can do that through a template set function that takes the type of what they would like to store. The users retrieve them using a get function. There is not really a problem but i am wondering if there is a better way to do what i am trying to achieve.

Main.cpp

#include <iostream>
#include <unordered_map>
#include <any>

class Arguments {
private:
    std::unordered_map<std::string, std::any> Values;
public:
    template<typename ReturnType>
    ReturnType get(std::string SearchValue);
    
    template<typename ValueType>
    void add(std::string Key);
};

template<typename ReturnType>
ReturnType Arguments::get(std::string Search)
{
    return std::any_cast<ReturnType>(Values[Search]);
}

template<typename ValueType>
void Arguments::add(std::string Key) 
{
    Values[Key] = ValueType{};
}

int main() 
{
    Arguments Example;

    Example.add<int>("ints");

    std::cout << Example.get<int>("ints") << "\n";

    // i want to be able to:
    //std::cout << Example.get("ints") << "\n"; 
    // and have it know to return an int
    return 0;
}

The problem

This works fine for what I am trying to do but is not optimal. I am using std::any but have also tried std::variant which is an option but std::any is simpler for showing what i mean. I am wondering if there is a better way to implement this sort of thing. Because this is a library I want simple ways to interface with this code. When someone calls get() they have to specify the type they want it to be returned as, this is a problem for my program. Is there a way to have get return the type of the value in the map? Or is there a better way I can structure my program to eliminate this?

I have just researched c++ meta programming techniques and features in hopes that something would be useful in this case, but i found nothing. I looked into meta programming libraries like boosts hana, mpl and fusion but if they could be used to solve my case i could not work out how. I also tried to have a function that would return auto but that did not work because it could not return more than one type.


Solution

  • Basically what you want is impossible in C++, since compiler should know return type of function at compile-time and you want it to be dependent from run-time.

    But it still possible somewhat, but with a catch. You should get values based not on plain names, but rather some type-dependent argument. I.e. something like this:

    #include <iostream>
    #include <unordered_map>
    #include <any>
    
    template<typename T>
    struct ArgName {
      std::string name;
    };
    
    class Arguments {
    private:
        std::unordered_map<std::string, std::any> Values;
    public:
        template<typename ReturnType>
        ReturnType get(ArgName<ReturnType> SearchValue);
        
        template<typename ValueType>
        void add(ArgName<ValueType> Key);
    };
    
    template<typename ReturnType>
    ReturnType Arguments::get(ArgName<ReturnType> Search)
    {
        return std::any_cast<ReturnType>(Values[Search.name]);
    }
    
    template<typename ValueType>
    void Arguments::add(ArgName<ValueType> Key) 
    {
        Values[Key.name] = ValueType{};
    }
    
    int main() 
    {
        Arguments Example;
        ArgName<int> ints("ints");
    
        Example.add(ints);
    
        std::cout << Example.get(ints) << "\n";
    
        return 0;
    }