Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to translate `void(*fn)(const char *, ...)` to `std::function` and vice versa

typedef void(*fn1)(const char *, ...);
typedef std::function<void(const char *, ...)> fn2; // has initializer but incomplete type

Intuitively, these are the effectively the same to me, but obviously my intuition is failing me. How would I reconcile these data types?

  1. How is fn2 an incomplete type?
  2. What changes are necessary to the signature of fn2, to allow me to assign it a type of fn1?
  3. When creating a lambda to assign to fn2, how do I access the variadic argument list?

    In other words, what is the lambda equivalent to the following?

    void fn1_compatible (const char * format, ...) {
      va_list args;
      va_start(args, format);
      //TODO: Do stuff with variadic arguments
      va_end(args);
    }
    

NOTE: As an aside, these signatures are related to logging, but please answer the question in a general (not logging) context.

like image 704
Zak Avatar asked Mar 29 '19 18:03

Zak


1 Answers

Variadic functions are not supported by std::function. std::function takes one type, and it looks something like this:

template<class>
class function;  // Intentionally incomplete

template<class Ret, class... Args>
class function<Ret(Args...)> {
    // Deduce return type and argument types from function type
};

But this does not deduce the types for variadic function. So void(const char*) would work (Ret is void and Args... is const char*), but void(const char*, ...) would not work (As that would need to be deduced from Ret(Args..., ...))

To make a functor object out of it, either just use a bare function pointer, like you did with fn1, or make do what the C standard library does with functions like vprintf:

decltype(auto) my_variadic_function(const char * format, ...) {
  va_list args;
  va_start(args, format);
  try {
      auto&& return_value = vmy_variadic_function(format, args);
  } catch (...) {
      va_end(args);
      throw;
  }
  va_end(args);
  return std::forward<decltype(return_value)>(return_value);
}

void vmy_variadic_function(const char* format, va_list args) {
    // Do stuff with args
}

And then pass vmy_variadic_function in a std::function<void(const char*, va_list)>.

like image 145
Artyer Avatar answered Sep 20 '22 13:09

Artyer