3

In our codebase we have callbacks that are stored using (eg. std::function<void()>). Sometimes we would like to bind a function with a different signature to the callback which can be done using bind. This is fine for different function parameters but trying to bind a function that returns something to a callback that is expecting a void return doesn't work, see here.

The simplest solution we have found is to cast the bound function's signature to a function with the same parameters but a void return type:

#include <functional>
#include <iostream>

int ReturnInt()
{
  std::cout << "ReturnInt" << std::endl;
  return 5;
}

struct Struct
{
  int ReturnInt()
  {
    std::cout << "Test::Func" << std::endl;
    return 30;
  }
};

template<typename ReturnType, typename ... ArgumentTypes>
auto IgnoreResult(ReturnType (*i_Func)(ArgumentTypes ...))
  -> void (*)(ArgumentTypes ...)
{
  return reinterpret_cast<void (*)(ArgumentTypes ...)>(i_Func);
}

template<typename ReturnType, typename ClassType, typename ... ArgumentTypes>
auto IgnoreResult(ReturnType (ClassType::*i_Func)(ArgumentTypes ...))
  -> void (ClassType::*)(ArgumentTypes ...)
{
  return reinterpret_cast<void (ClassType::*)(ArgumentTypes ...)>(i_Func);
}


int main(int argc, char **argv)
{
  std::function<void ()> BoundType;

  Struct instance;
  BoundType = std::bind(IgnoreResult(&Struct::ReturnInt), &instance);
  BoundType();

  BoundType = std::bind(IgnoreResult(&ReturnInt));
  BoundType();

  return 0;
}

This has been tested with Visual Studio 2013 November CTP, cygwin clang 3.4.2 and cygwin gcc 4.8.3 and works on all platforms but calling a function pointer that has been cast to a different function signature is undefined behaviour.

I know that certain calling conventions could break it but as far as I could tell from the Microsoft calling conventions the return type is passed via a register not via the stack. We are also never specifying different calling conventions and always use the defaults.

Assuming that the gcc, clang and Microsoft compilers don't change their behaviour, is this a safe method to use to ignore the return type of a function when binding a callback?

Community
  • 1
  • 1
fun4jimmy
  • 672
  • 4
  • 16
  • 1
    Why don't simply ignore the result after return, like `(void) ReturnInt();`? – CiaPan Aug 20 '14 at 09:47
  • 2
    So your question is whether it is safe to rely on undefined behavior ? – quantdev Aug 20 '14 at 09:47
  • How about functions that return an object? The resulting object may be allocated by a caller and then constructed by a callee; if the caller is forced to ignore the declared result type of a callee, it won't allocate the object - what will then happen? – CiaPan Aug 20 '14 at 09:50
  • @CiaPan ignore it where? The callback has to have a void return type and in our codebase there are multiple functions with differing signatures all bound to the same callback. – fun4jimmy Aug 20 '14 at 09:52
  • @quantdev I guess my question is more like under what situations would this currently break with **gcc**, **clang** and **Microsoft** compilers. I know it's not safe to rely on undefined behaviour but this seems like the simplest solution for our current solution. – fun4jimmy Aug 20 '14 at 09:53
  • @CiaPan That situation doesn't happen in our code, I can see how that would be a problem if this were used in that way though. – fun4jimmy Aug 20 '14 at 09:54
  • 1
    Why not simply provide a wrapper that you can bind to that calls your functions? That way your code isn't filed with caveats, predictions, and uncertainties? – H Walters Aug 20 '14 at 10:17
  • @HWalters That's definitely the most robust option. The definition of the wrapper is more involved than the definition of `IgnoreResult` above. We are also currently stuck using a compiler without variadic template support so will have to provide many overloads for differeing number of argument types. I thought I'd canvas some opinions before we decide which solution to use. – fun4jimmy Aug 20 '14 at 10:26

3 Answers3

4

You can use lambdas:

#include <functional>
#include <iostream>

int ReturnInt()
{
  std::cout << "ReturnInt" << std::endl;
  return 5;
}

struct Struct
{
  int ReturnInt()
  {
    std::cout << "Test::Func" << std::endl;
    return 30;
  }
};

int main(int argc, char **argv)
{
  std::function<void ()> BoundType;

  Struct instance;
  BoundType = [&instance] { instance.ReturnInt(); };
  BoundType();

  BoundType = [] { ReturnInt(); };
  BoundType();

  return 0;
}
Drax
  • 12,682
  • 7
  • 45
  • 85
4

Is it safe to change a function pointers signature and call it to ignore the return type?

No, it is not, c.f. the C++ standard, section § 5.2.10 [expr.reinterpret.cast]

The effect of calling a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition of the function is undefined.

Even if it does appear to work on a particular compiler/platform, nothing guarantees you that it really does (hidden side effects, stack corruption...).

You should consider a new design where this cast is not needed in the first place (hard to answer without more context)

quantdev
  • 23,517
  • 5
  • 55
  • 88
3

You might solve your problem using an approach similar to that employed in libsigc++2 (via sigc::hide_return()).