3

How can I save a pointer to a function without save its return type?
For example:

int GetInt() { return 5; }
string GetStr() { return "abc"; }

FunctionPointerClass GetAny;

int main()
{
    GetAny = &GetInt;
    auto var = GetAny();

    GetAny = &GetStr;
    auto var2 = GetAny();

    cout << var << '\n' << var2;
}

Edit

A simple way to do this is use variant<> (thanks @sehe), like this:

#include <boost/variant.hpp>
#include <string>
#include <iostream>
#include <functional>

int         GetInt() { return 5;     }
std::string GetStr() { return "abc"; }

int main()
{
    std::function<boost::variant<int, std::string>()> Get;
    Get = &GetInt;
    std::cout << Get() << '\n';

    Get = &GetStr;
    std::cout << Get() << '\n';
}

But, it not too applicable for my project: a non-typed class. To use it, I will need stack all the used return types, to put it in template of variant<>. Like this:

class Var {
  private:
    void* _val;

    template <typename T>
    T& _Get() const {
        return *((T*)_val);
    }

    // Static stack variable HERE

  public:
    val() {}

    template <typename T>
    val(T val) {
        Set(val);
    }

    ~val() {
        if(_val != nullptr) delete _val;
    }

    std::function<boost::variant</*Stack*/>()> Get;

    template <typename T>
    void Set(T val) {
        if(_val != nullptr) delete _val;

        _val = new T(val);
        Get = &_Get<T>;

        // TODO Add 'T' to Stack
    }
};

How can I do this?

Felipe Nascimento
  • 207
  • 1
  • 4
  • 10
  • Right, so like my answer/coment says, what is the ACTUAL problem you're trying to solve here. Why do you need a function pointer into a class? – Mats Petersson Sep 20 '17 at 05:54
  • @MatsPetersson I want a class that hold any type of variable and cast it without explicit the data type. Why? The challenge. – Felipe Nascimento Sep 20 '17 at 08:55
  • Yes, but to use such a container class, you still need to know the actual content, meaning you may just as well just use the the `T Get() { return *(T*)value; }` variant. The recieving code will need to know what type it is to use it - or even to accept a copy in case of complex types that have copy-construction (e.g. std::string). – Mats Petersson Sep 21 '17 at 06:52

2 Answers2

6

Not exactly.

  • You can of course make it a function to print the value.

  • Or you can use std::variant/boost::variant to return either type.

  • Other techniques, like Type Erasure might also apply.

I flesh the last two of the approaches here:


Using variant<>

Live On Coliru

#include <boost/variant.hpp>
#include <string>
#include <iostream>
#include <functional>

int         GetInt() { return 5;     }
std::string GetStr() { return "abc"; }

int main()
{
    std::function<boost::variant<int, std::string>()> Get;
    Get = &GetInt;
    std::cout << Get() << '\n';

    Get = &GetStr;
    std::cout << Get() << '\n';
}

Prints

5
abc

Using type erasure

A related technique is type erasure, where you define a "concept" with supported operations (in this case, output streaming) and you hide it behind a polymorphic interface. E.g:

struct Printable {
    template <typename T> Printable(T v) : _stored(new concrete<T>(v)) { }

    friend std::ostream& operator<<(std::ostream& os, Printable const& p) {
        return p._stored->print(os);
    }

  private:
    struct interface {
        virtual std::ostream& print(std::ostream& os) const = 0;
        virtual ~interface() = default;
    };

    template <typename T>
    struct concrete : interface {
        concrete(T v) : v(v) {}
        virtual std::ostream& print(std::ostream& os) const override {
            return os << v;
        }
        T v;
    };

    std::unique_ptr<interface> _stored;
};

In that case you can make the whole program:

Live On Coliru

int         GetInt() { return 5;     }
std::string GetStr() { return "abc"; }

int main()
{
    std::function<Printable()> Get;
    Get = &GetInt;
    std::cout << Get() << '\n';

    Get = &GetStr;
    std::cout << Get() << '\n';
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Print the value is just an example – Felipe Nascimento Sep 19 '17 at 22:42
  • That's why I said the rest. See the added live sample – sehe Sep 19 '17 at 22:43
  • I don't understood it at well. Is the return type of function pointer a `boost::variant`? – Felipe Nascimento Sep 19 '17 at 22:48
  • Yes. You don't have to expect to understand it right away. Most likely variants are a new concept, take your time considering it. – sehe Sep 19 '17 at 22:49
  • I added another approach using type erasure instead. Both approaches have pros/cons. You decide what trade-off you need: **[Live On Coliru](http://coliru.stacked-crooked.com/a/bf30bfd9052786f3)** as well – sehe Sep 19 '17 at 22:58
  • Using `variant<>`, can I do a stack of types? For example, in my script I use `std::string`, `int`, `float` and an user class in my `variant<>`, so, its type will be `variant` automatically. – Felipe Nascimento Sep 19 '17 at 23:21
  • Yes, however if you have a lot of types to support then consider using [`boost::any`](http://www.boost.org/doc/libs/1_61_0/doc/html/any.html)/[`std::any`](http://en.cppreference.com/w/cpp/utility/any) instead. – Remy Lebeau Sep 19 '17 at 23:22
  • @RemyLebeau Actually, let's not recommend `any`. It's an antipattern 99.9% of the time. To be frank, the question might hide a code smell, and we don't want solve it by heaping on eau-de-cologne – sehe Sep 19 '17 at 23:26
  • @RemyLebeau, in `any`, I need to cast the value using correct type calling `any_cast(any)`, and I'm tying to avoid it – Felipe Nascimento Sep 19 '17 at 23:28
  • @FelipeNascimento: [How to print boost::any to a stream?](https://stackoverflow.com/questions/3224915/) – Remy Lebeau Sep 19 '17 at 23:36
  • @RemyLebeau, print is just a sample of use. I edited the question – Felipe Nascimento Sep 19 '17 at 23:43
  • @RemyLebeau Precisely. And the [only palatable answer there](https://stackoverflow.com/a/38002757/85371) uses... Type Erasure (that time using Boost Type Erasure). With any, the contract is much too wide. 99% of the time – sehe Sep 19 '17 at 23:44
1

I was going to write this as a comment, and it's not REALLY an answer, but it's a lengthy discussion on the subject of "return different types from the a function with the same name".

C++ doesn't take return type into concideration for overloading functions. In other words, std::string GetAny() and int GetAny() are considered as duplicates of the same function, because they only differ in return type. That's a restriction in the language definition, and you have to work around this restriction by "doing something other than return two different types".

As discussed in another answer, one solution is boost::variant, which is basically a way to define a class that can have multiple different types inside it, which has a type of "tag" to determine what it really contains, and various other clever stuff related to that. Makes it very neat in many ways.

However, it is only really useful for certain classes of problems. In many cases, your code still needs to understand what the data-type is, and having a function that may return a string, an integer or any other "random" data type isn't meaningful. Yes, it's handy to be able to define function pointers that return "any" type - but only in so far as you can have a table of the functions. Your code will not really work well if you do:

std::string s;
s += GetAny();   // Happens to be returning `int` in this call.

Just as bad:

int x = GetAny();    // Returning a string...

So, whilst you can write code that may return "any type", it's hard to make use of such a function without knowing what it returns. I've been programming professionally for over 30 years, and I have used function pointers for many things. So far, I've managed to not need to use this more than a handful times, and every time, it's been some form of solution similar to boost::variant (essentially returning a data-structure, where one field is describing the data-type itself). The two cases I can think of where I have used this are in my Lisp interpreter and my Basic intrepreter. They need a "variable type" that have the ability to hold various types of objects (integer, float, string, list [only in Lisp]). In my Pascal compiler, I do have a proper type system, so it doesn't need to have multiple types returned from a single function (or function pointer). I'd say "it smells funny" when this type of situation happens, and you should probably think about what it is you're trying to solve, and if this is really the right solution.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227