muhzi
muhzi

Reputation: 129

returning a generic type with template functions

I'm new to using templates and I want to make a function that returns generic types. I made a very simple template as such

enum tag { SHORT, INT, FLOAT, DOUBLE };
template <typename T>
T get_val(tag t){
    switch (t) {
    case SHORT: return .. //returns a short;
    case INT: return .. //returns a int;
    case FLOAT: return .. //returns a float;
    case DOUBLE: return .. //returns a double;
    }
}

the problem is that floats and doubles are returned as integer numbers. so I wonder why this happens and if there's a way I can use it properly.

Upvotes: 1

Views: 2677

Answers (2)

Alex Huszagh
Alex Huszagh

Reputation: 14614

I would recommend doing this with a variant and runtime checks for the type among a limited subset of possible types:

Use a public interface taking a variant with an enum for the correct type (this is a naive version, you should really use std::variant and get_if):

enum variant_type
{
    variant_null,
    variant_int,
    variant_float,
    variant_double,
}


class variant
{
public:
    // Add constructors with the correct type for the
    // underlying storage
    variant() = default;
    variant(int x);
    variant(float x);
    variant(double x);

    // ensure you can check for empty variants and check the type
    explicit operator int() const;
    explicit operator float() const;
    explicit operator double() const;
    explicit operator bool() const;
    variant_type type() const;

private:
    variant_type type_ = variant_null;
    void* data_ = nullptr;
};

Then, add many custom function or method overloads that allow you to choose the correct method at runtime:

class my_handler
{
public:
    my_handler(const variant& v)
    {
        switch (v.type()) {
            case variant_null;
                open(nullptr);      break;
            case variant_int;
                open(int(v));       break;
            case variant_float;
                open(float(v));     break;
            case variant_double;
                open(double(v));    break;
        }
    }

private:
    void open(std::nullptr_t nullp);
    void open(int x);
    void open(float x);
    void open(double x);
};

This allows you to have a public interface that is generic for a limited subset of types, but that allows you to avoid having to know the correct type at compile-time.

This can all be done much more idiomatically using std::variant and get_if rather than writing your own custom variant class. I just this as an (inefficient) example of how many libraries (like the Windows API and Qt) have implemented their own.

Upvotes: 2

R Sahu
R Sahu

Reputation: 206577

You may not use a different return type using a switch statement like that.

However, if the input to get_val is known at compile time, you can use template metaprogramming techniques to get what you need.

enum tag { SHORT, INT, FLOAT, DOUBLE };

template <tag t> struct return_type_chooser;

template <> struct return_type_chooser<SHORT>
{
   using type = short;
};

template <> struct return_type_chooser<INT>
{
   using type = int;
};

template <> struct return_type_chooser<FLOAT>
{
   using type = float;
};

template <> struct return_type_chooser<DOUBLE>
{
   using type = double;
};

template <tag t>
typename return_type_chooser<T>::type get_val()
{
   ...
}

You can use:

get_val<SHORT>()

but not

get_val(SHORT)

Update, in response to OP's comment

If the input to get_val is known only at run time, std::any (C++17) or boost::any should work as the return type.

Upvotes: 4

Related Questions