0

I have some callback definition like:

using TSomeCallback = std::function<ReturnType(Type1, Type2, Type3, …)>;

Also I have some method that accepts this callback as an argument:

void SetCallback(TSomeCallback);

All I want is to define some template that instantiates no-op callback directly from TSomeCallback:

void SetCallback(TSomeCallback = GetNoopCallback<TSomeCallback>());  // or
void SetCallback(TSomeCallback = GetNoopCallback(TSomeCallback()));  // or any other way.

I found this answer - it's good, but I don't know how to improve it for my purpose.

abyss.7
  • 13,882
  • 11
  • 56
  • 100

4 Answers4

4

You can do it this way

void SetCallback(TSomeCallback = [](auto...){});

but I would recommend rather

void SetCallback(TSomeCallback = {})

and check for the callback being non-empty inside. Or perhaps don't supply a default parameter and just don't call SetCallback if you don't want to be called back!

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • even with the default the `std::function` might be empty, hence `{}` is the best suggestion imho. ( fwiw: https://en.cppreference.com/w/cpp/utility/functional/function/operator_bool) – 463035818_is_not_an_ai Mar 31 '23 at 09:16
  • @463035818_is_not_a_number An empty std::function is different from a "noop" std::function (An empty std::function is like `[](auto&&...) { throw std::bad_function_call{}; }`) – Artyer Mar 31 '23 at 12:11
  • @Artyer yes. Maybe the comment was not clear. Without the default: `void SetCallback(TSomeCallback)` can be called with an empty / default constructed `std::function`. The noop OP is asking for is false security ("its always a valid function" but it isnt really). Hence I think the best is to use `{}` as default and check for emptyness (because the check for emptyness should be there anyhow) – 463035818_is_not_an_ai Mar 31 '23 at 12:16
3

You can use a lambda to construct a std::function< ... > that does nothing. Provided, the return type is void then this works:

#include <functional>
#include <string>
using namespace std;
     
using TSomeCallback = std::function<void(int,double,int)>;
     
int main() {
    TSomeCallback tc = [](auto...){};
    tc(42,12.0,42);
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
2

You can implement GetNoopCallback for void-returning functions like this:

template <class F>
F GetNoopCallback() {
    return [](auto&&...){};
}

Then:

void SetCallback(TSomeCallback = GetNoopCallback<TSomeCallback>());
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
1
#include <type_traits>

template <typename result>
requires (std::is_void_v<result> ||
          std::is_default_costructible_v<result>)
constexpr auto return_default = [] (auto&& ...)
noexcept (std::is_void_v<result> ||
          std::is_nothrow_default_costructible_v<result>)
{
    if  constexpr (std::is_void_v<result>)
        return;
    else
        return result {};
};

Now just cast or use the template variable as rvalue:

std::function<void(int)> f1 = return_default<void>;
auto f2 = std::function<A(B,C)>{return_default<A>};

You can take one step further to remove the need for a return type:

struct any_function_default{
    template<typename result, typname ... args>
    requires (std::is_void_v<result> ||
              std::is_default_costructible_v<result>)
    constexpr operator std::function<result(args ...)> () const {
        return [] (auto&& ...) {
               if  constexpr (std::is_void_v<result>)
                   return;
               else
                   return result {};
        };
    };
};

Now use any_function_default as the universal default for functions:

std::function<void(int)> f1 = any_function_default{};
auto f2 = std::function<A(B,C)> { any_function_default{} };
Red.Wave
  • 2,790
  • 11
  • 17