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?

3
  • 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? Commented Sep 20, 2017 at 5:54
  • @MatsPetersson I want a class that hold any type of variable and cast it without explicit the data type. Why? The challenge. Commented Sep 20, 2017 at 8: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). Commented Sep 21, 2017 at 6:52

2 Answers 2

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';
}
Sign up to request clarification or add additional context in comments.

12 Comments

Print the value is just an example
That's why I said the rest. See the added live sample
I don't understood it at well. Is the return type of function pointer a boost::variant?
Yes. You don't have to expect to understand it right away. Most likely variants are a new concept, take your time considering it.
I added another approach using type erasure instead. Both approaches have pros/cons. You decide what trade-off you need: Live On Coliru as well
|
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.