How can set different function signature to the same function pointer?

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-11 04:08:18

问题


How can I set a function pointer depending on some condition to functions with different signature?

Example:

short int A() 
{
    return 0;
}

long int B()
{
    return 0;
}

void main()
{
   std::function<short int()> f = A;
   f();
   if(true)
   {
      //error
      f = B;
   }
}

How can use the same function pointer for two functions with different signature? Is it possible?

If is not, there is an efficient way to call the appropriate function depending on behavior instead of use a variable and split the whole code with if statements?

EDIT / EXPANSION ("2nd case")

#include <SDL.h>

class Obj { //whatever ...}

class A 
{
private:
    Uint16 ret16() { return SDL_ReadLE16(_pFile); }
    Uint32 ret32() { return SDL_ReadLE32(_pFile); }
    _pFile = nullptr;
public:
    Obj* func() 
    {
        Obj obj = new Obj();
        _pFile = SDL_RWFromFile("filename.bin","r"));
        auto ret = std::mem_fn(&SHPfile::ret16); 
        if(true)
        {
            ret = std::mem_fn(&SHPfile::ret32);          
        }

        //ret();
        // continue whatever
        // ....
        SDL_RWclose(_pFile);
        return *obj;
    }
}

I have a compilation error on a similar case using the Uint16 and Uint32 variable of SDL 2 library, using std::mem_fn

the compiler give me this error (relative to my code, but it's implemented in a way like the above example):

error: no match for ‘operator=’ (operand types are ‘std::_Mem_fn<short unsigned int (IO::File::*)()>’ and ‘std::_Mem_fn<unsigned int (IO::File::*)()>’)

To resolve this compilation error, I forced both the function to return a int type.

Is there a better way?

Or I did something wrong?


回答1:


The comments already say that clang accepts the code as is, and I can now say that GCC 4.8.4 and GCC 4.9.2 both accept it as well, after fixing void main() to say int main().

This use of std::function is perfectly valid. The C++11 standard says:

20.8.11.2 Class template function [func.wrap.func]

function& operator=(const function&);
function& operator=(function&&);
function& operator=(nullptr_t);

There is no template assignment operator here, so assignment of B could only construct a new temporary function<short int()> object, and move-assign from that. To determine whether the construction of that temporary is possible:

20.8.11.2.1 function construct/copy/destroy [func.wrap.func.con]

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

7 Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

20.8.11.2 Class template function [func.wrap.func]

2 A callable object f of type F is Callable for argument types ArgTypes and return type R if the expression INVOKE(f, declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause 5), is well formed (20.8.2).

20.8.2 Requirements [func.require]

2 Define INVOKE(f, t1, t2, ..., tN, R) as INVOKE(f, t1, t2, ..., tN) implicitly converted to R.

1 Define INVOKE(f, t1, t2, ..., tN) as follows:

  • ... (all related to pointer-to-member types)
  • f(t1, t2, ..., tN) in all other cases.

In short, this means that std::function<short int()> can be used with any function that can be called with no arguments, and which has a return type that can be implicitly converted to short. long clearly can be implicitly converted to short, so there is no problem whatsoever.

If your compiler's library doesn't accept it, and you cannot upgrade to a more recent version, one alternative is to try boost::function instead.

Aaron McDaid points out lambdas as another alternative: if your library's std::function is lacking, you can write

std::function<short int()> f = A;
f = []() -> short int { return B(); };

but if you take this route, you can take it a step further and avoid std::function altogether:

short int (*f)() = A;
f = []() -> short int { return B(); };

This works because lambas that don't capture anything are implicitly convertible to a pointer-to-function type that matches the lambda's arguments and return type. Effectively, it's short for writing

short int B_wrapper() { return B(); }
...
f = B_wrapper;

Note: the conversion from long to short may lose data. If you want to avoid that, you can use std::function<long int()> or long int (*)() instead.




回答2:


No, you can't do that in a statically typed language unless your types all have a common super type, and C++ doesn't have that for primitives. You would need to box them into an object, then have the function return the object.

However, if you did that, you may as well just keep an object pointer around and use that instead of a function pointer, especially since it's going to make it easier to actually do something useful with the result without doing casts all over the place.

For example, in a calculator I wrote in Java, I wanted to work with BigInteger fractions as much as possible to preserve precision, but fallback to doubles for operations that returned irrational numbers. I created a Result interface, with BigFractionResult and DoubleResult implementations. The UI code would call things like Result sum = firstOperand.add(otherOperand) and didn't have to care which implementation of add it was using.




回答3:


The cleanest option that comes to mind is templates:

#include <iostream>
using namespace std;

template <typename T>
T foo() {
    return 0;
}

int main() {
    long a = foo<long>();
    cout << sizeof a << " bytes with value " << a << endl;

    int b = foo<int>();
    cout << sizeof b << " bytes with value " << b << endl;

    short c = foo<short>();
    cout << sizeof c << " bytes with value " << c << endl;

    return 0;
}

In ideone.com this outputs:

4 bytes with value 0
4 bytes with value 0
2 bytes with value 0

Hopefully this is what you needed.


If for some reason you really need to pass an actual function around, I would recommend looking into std::function and trying to write some template code using that.



来源:https://stackoverflow.com/questions/28167166/how-can-set-different-function-signature-to-the-same-function-pointer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!