Chinmoy Kulkarni
Chinmoy Kulkarni

Reputation: 317

Deduce return type of a function dynamically

I have a few functions defined which have return types of void, int, float, float*, etc(some classes aswell).

My cmd is a vector of strings inputed from user. where 0th location is the function name(read_lib, square, open_file) and the 1st location is the argument(/path/to/file , number_to_square) etc.

auto find_and_execute(vector<string> cmd){
//for(auto x: cmd){cout << x << endl;}
if(cmd.at(0) == "square") {return square(stoi(cmd.at(1)));} // unsigned_int
if(cmd.at(0) == "cube") {return cube(stoi(cmd.at(1)));}     // unsigned_int
if(cmd.at(0) == "open_file") {open_file(cmd.at(1));}        //void
if(cmd.at(0) == "read_lib") {read_lib(cmd.at(1));}          //void
if(cmd.at(0) == "read_verilog") {read_verilog(cmd.at(1));}  //void
if(cmd.at(0) == "set_top") {set_top(cmd.at(1));}            //void
if(cmd.at(0) == "get_pin") {return get_pin(cmd.at(1));}     // Pin Class object takes in cell argument
}

Error: inconsistent deduction for 'auto': 'unsigned int' and then 'Pin'

Edit: I have another question. All my functions do not take in string input as argument. I could convert string to integer but how do I convert it to some class object like Pin/Cell

Upvotes: 3

Views: 427

Answers (1)

Xirema
Xirema

Reputation: 20386

The return type of a function must be able to be determined at compile-time. C++ is a statically-typed language. auto does not mean "could be anything", it means "will be deduced at compile-time".

If you have a function which needs to potentially return multiple types, you'll want to use std::variant (in C++17) or boost::variant (pre-C++17, but requires use of the Boost Library).

In your case specifically, since some of your calls can return nothing (as demarcated by void), it might also be good to place this variant inside an optional (also C++17, or boost::optional if pre-C++17):

using return_t = std::optional<std::variant<unsigned int, Class>>;

return_t find_and_execute(std::vector<std::string> const& cmd) {
    if(cmd.at(0) == "square") {return square(stoi(cmd.at(1)));} // unsigned_int
    if(cmd.at(0) == "cube") {return cube(stoi(cmd.at(1)));}     // unsigned_int
    if(cmd.at(0) == "open_file") {open_file(cmd.at(1)); return {};}        //void
    if(cmd.at(0) == "read_lib") {read_lib(cmd.at(1)); return {};}          //void
    if(cmd.at(0) == "read_verilog") {read_verilog(cmd.at(1)); return {};}  //void
    if(cmd.at(0) == "set_top") {set_top(cmd.at(1)); return {};}            //void
    if(cmd.at(0) == "get_pin") {return get_pin(cmd.at(1));}     // Class object
}

return_t result = find_and_execute({std::string("square"), std::string("13")});
if(result) {//Should always be true
    try {
        unsigned int & value = std::get<unsigned int>(*result);
    } catch (std::bad_variant_access const&) {}
}

result = find_and_execute({std::string("open_file"), std::string("File.txt")});
if(!result) {//Should always be true
    /*...*/
}

result = find_and_execute({std::string("get_pin"), std::string("EAX")});
if(result) {//Should always be true
    try {
        Class & value = std::get<Class>(*result);
    } catch (std::bad_variant_access const&) {}
}

EDIT:

An alternate version, as suggested by @chris, uses std::monostate to avoid the use of std::optional. This may be a better interface for you, depending on the circumstances.

using return_t = std::variant<std::monostate, unsigned int, Class>;

return_t find_and_execute(std::vector<std::string> const& cmd) {
    if(cmd.at(0) == "square") {return square(stoi(cmd.at(1)));} // unsigned_int
    if(cmd.at(0) == "cube") {return cube(stoi(cmd.at(1)));}     // unsigned_int
    if(cmd.at(0) == "open_file") {open_file(cmd.at(1)); return {};}        //void
    if(cmd.at(0) == "read_lib") {read_lib(cmd.at(1)); return {};}          //void
    if(cmd.at(0) == "read_verilog") {read_verilog(cmd.at(1)); return {};}  //void
    if(cmd.at(0) == "set_top") {set_top(cmd.at(1)); return {};}            //void
    if(cmd.at(0) == "get_pin") {return get_pin(cmd.at(1));}     // Class object
}

return_t result = find_and_execute({std::string("square"), std::string("13")});
try {
    unsigned int & value = std::get<unsigned int>(result);
} catch (std::bad_variant_access const&) {}

result = find_and_execute({std::string("open_file"), std::string("File.txt")});
//Could query for it if you really needed to
//try {
    //std::monostate & value = std::get<std::monostate>(result);
//} catch (std::bad_variant_access const&) {}

result = find_and_execute({std::string("get_pin"), std::string("EAX")});
try {
    Class & value = std::get<Class>(*result);
} catch (std::bad_variant_access const&) {}

Upvotes: 9

Related Questions