How would one call std::forward on all arguments in a variadic function?
You would do:
template <typename ...Params>
void f(Params&&... params)
{
y(std::forward<Params>(params)...);
}
The ...
pretty much says "take what's on the left, and for each template parameter, unpack it accordingly."
Forward variadic function arguments to another variadic function without cost
If we have c++11, why mess around with variadic argument lists?
#include <utility>
extern enum {LOG_MODE_LOCAL, LOG_MODE_REMOTE} m_logMode;
extern void LogDebugEx(const char*, ...);
template<class...Args>
void LogDebug(const char* zFormat, Args&&...args)
{
if (m_logMode == LOG_MODE_LOCAL) // Does not need to optimize this mode.
{
char zDesc[5000];
snprintf(zDesc, 5000, zFormat, args...);
// do what you have to do here
}
else // m_logMode == LOG_MODE_REMOTE, critical path
{
LogDebugEx(zFormat, std::forward<Args>(args)...); // Forwarded to new variadic function
}
}
Forward a specific range of arguments in a variadic function
Use an additional template parameter:
template <typename T, typename... Ts>
void test_variadic(T&& arg, Ts&&... args) {
test_simple(std::forward<Ts>(args)...);
}
That way, the first parameter is not part of the variadic.
how to forward variable number of arguments to another function?
The typical formulation of this in C is to have two functions, one that accepts ...
and one that accepts a va_list
(e.g., printf
versus vprintf
). In C++ it’s convenient to do this with overloads:
// public
void Logger::Error(const std::string& format, ...) {
va_list args;
va_start(args, format);
Error(format, args);
va_end(args);
}
// private
void Logger::Error(const std::string& format, va_list args) {
if (this != &errorsLogger)
errorsLogger.Error(format, args);
vfprintf(logFile, format.c_str(), args);
fprintf(logFile, "\n");
fflush(logFile);
}
Using C++11, it is possible to do directly with a variadic template. You can also forward arguments to C-style variadic functions.
template<class... Args>
void Logger::Error(const std::string& format, Args&&... args) {
if (this != &errorsLogger)
errorsLogger.Error(format, std::forward<Args>(args)...);
fprintf(logFile, format.c_str(), std::forward<Args>(args)...);
fprintf(logFile, "\n");
fflush(logFile);
}
Forwarding variadic list of arguments
They are not the same. They are the same in the case that the arity of args
is 1 or 0. otherwise it will fail to compile, consider..
#include <iostream>
using namespace std;
template<typename T, typename ...U>
auto time_function_1(T&& func, U&& ...args)
{
std::cout<<"timing"<<std::endl;
auto val = std::forward<T>(func)(std::forward<U...>(args...));
std::cout<<"timing over"<<std::endl;
return val;
}
template<typename T, typename ...U>
auto time_function_2(T&& func, U&& ...args)
{
std::cout<<"timing"<<std::endl;
auto val = std::forward<T>(func)(std::forward<U>(args)...);
std::cout<<"timing over"<<std::endl;
return val;
}
int f (int){return 0;}
int y (int,int){return 0;}
int main() {
time_function_1(f,1);
time_function_2(f,1);
time_function_1(y,1,2); // fail
time_function_2(y,1,2);
return 0;
}
Demo
for the failing case std::forward<U...>(args...)
expands to forward<int, int>(int&, int&)
and will fail to compile.
std::forward<U>(args)...
expands to std::forward<int>(int&),std::forward<int>(int&)
How to pass variadic function parameters to another function in C++ (specifically std::format)?
You don't want the C-style ...
parameter. You want a variadic template:
template <typename T, typename ...P>
void log(T &&format, P &&... params)
{
std::string msg = fmt::format(std::forward<T>(format), std::forward<P>(params)...);
std::cout << msg << '\n';
}
Notice forwarding references for the parameter, pack instead of const references. Const references had caused problems for me before (spdlog used to use const references, and fmt::join
didn't like them).
Notice the first parameter being templated. At least with libfmt, this is necessary to be able to receive FMT_STRING(...)
parameters. With std::format
, an std::string_view
would probably suffice. const std::string &
is uncool because it forces a heap allocation.
Also I'm not sure how I feel about having a separate parameter-less overload. It means that you have to escape {
and }
only when there's at least one parameter, which is not nice.
Also consider using spdlog, which is very similar. It wraps libfmt, and can direct output to various/arbirary targets.
Forward variadic arguments based on conditions
Your question muddies the waters a bit, because you're mixing runtime and compile-time logic, because in your case the number and types of function arguments depends on the runtime value of something.
You're now stuck with two different options: it is possible to make this work (even in just C++14), but any call site that doesn't pass the correct arguments will not be detected until runtime. The way to do so is with the help of std::enable_if_t
, decltype()
and std::declval
. I've done this for your example, and the following compiles for me in C++14 mode:
#include <iostream>
#include <type_traits>
#include <stdexcept>
enum class Fruit {
Apple,
Orange,
Pear,
};
void processApple(int a, int b);
void processOrange(int c);
void processPear(double d);
void postProcessing();
struct Processor
{
template<typename... Ts, typename = decltype(processApple(std::forward<Ts>(std::declval<Ts>())...))>
static void process(std::integral_constant<Fruit, Fruit::Apple>, Ts&&... ts)
{
processApple(std::forward<Ts>(ts)...);
}
template<typename... Ts, typename = decltype(processOrange(std::forward<Ts>(std::declval<Ts>())...))>
static void process(std::integral_constant<Fruit, Fruit::Orange>, Ts&&... ts)
{
processOrange(std::forward<Ts>(ts)...);
}
template<typename... Ts, typename = decltype(processPear(std::forward<Ts>(std::declval<Ts>())...))>
static void process(std::integral_constant<Fruit, Fruit::Pear>, Ts&&... ts)
{
processPear(std::forward<Ts>(ts)...);
}
[[noreturn]] static void process(...)
{
// While this is inserted, it should never be called in practice,
// unless there's a programming error.
throw std::invalid_argument("Invalid argument specified to processFruit().");
}
};
template<typename... Args>
void processFruit(Fruit fruit_type, Args&&... args) {
switch (fruit_type) {
case Fruit::Apple:
Processor::process(std::integral_constant<Fruit, Fruit::Apple>(), std::forward<Args>(args)...);
break;
case Fruit::Orange:
Processor::process(std::integral_constant<Fruit, Fruit::Orange>(), std::forward<Args>(args)...);
break;
case Fruit::Pear:
Processor::process(std::integral_constant<Fruit, Fruit::Pear>(), std::forward<Args>(args)...);
break;
default:
break;
}
postProcessing();
return;
}
void processApple(int a, int b)
{
std::cout << "Apple: " << a << " // " << b << std::endl;
}
void processOrange(int c)
{
std::cout << "Orange: " << c << std::endl;
}
void processPear(double d)
{
std::cout << "Pear: " << d << std::endl;
}
void postProcessing()
{
std::cout << "Post processing" << std::endl;
}
int main()
{
processFruit(Fruit::Apple, 4, 8);
processFruit(Fruit::Orange, 15);
processFruit(Fruit::Pear, 1.234);
// The following will only throw an exception, but not fail to compile
//processFruit(Fruit::Pear, 1.234, 77);
return 0;
}
(The struct Processor
is just to clean up the namespace, they could be global functions.) Problem here is that the compiler can't detect a wrong call, see the commented out call to processFruit(Fruit::Pear, 1.234, 77);
that would generate an exception, but the compiler couldn't detect it at compile time.
In my eyes this is not very sensible though. Since the arguments depend on the Fruit
type anyway, I don't really see how you could even perform a call to one of these functions when the Fruit
argument is only known at runtime, because each call site will only work for a single type.
And if you do know that at compile time anyway, this could be done in a much simpler way via overloads, that also allows for much better compile-time diagnostics:
#include <iostream>
#include <type_traits>
enum class Fruit {
Apple,
Orange,
Pear,
};
void processApple(int a, int b);
void processOrange(int c);
void processPear(double d);
void postProcessing();
template<typename... Args>
void processFruit2Helper(std::integral_constant<Fruit, Fruit::Apple>, Args&&... args)
{
processApple(std::forward<Args>(args)...);
// or put the code of processApple directly in here
}
template<typename... Args>
void processFruit2Helper(std::integral_constant<Fruit, Fruit::Orange>, Args&&... args)
{
processOrange(std::forward<Args>(args)...);
// or put the code of processOrange directly in here
}
template<typename... Args>
void processFruit2Helper(std::integral_constant<Fruit, Fruit::Pear>, Args&&... args)
{
processPear(std::forward<Args>(args)...);
// or put the code of processPear directly in here
}
template<Fruit f, typename... Args>
void processFruit2(Args&&... args)
{
processFruit2Helper(std::integral_constant<Fruit, f>(), std::forward<Args>(args)...);
postProcessing();
}
void processApple(int a, int b)
{
std::cout << "Apple: " << a << " // " << b << std::endl;
}
void processOrange(int c)
{
std::cout << "Orange: " << c << std::endl;
}
void processPear(double d)
{
std::cout << "Pear: " << d << std::endl;
}
void postProcessing()
{
std::cout << "Post processing" << std::endl;
}
int main()
{
processFruit2<Fruit::Apple>(4, 8);
processFruit2<Fruit::Orange>(15);
processFruit2<Fruit::Pear>(1.234);
// The following will fail to compile here (which is good)
//processFruit2<Fruit::Pear>(1.234, 77);
return 0;
}
That all said, I suspect there's some higher-level design problem in your code, but we won't be able to understand that with the limited example you've provided.
Related Topics
The Fastest Way to Retrieve 16K Key-Value Pairs
Com(C++) Programming Tutorials
Std::Cin Doesn't Throw an Exception on Bad Input
Differencebetween If (Null == Pointer) VS If (Pointer == Null)
Why Does (1 << 31) >> 31 Result in -1
How to Typedef a Type and the Same Type's Pointer
How Does Intel Tbb's Scalable_Allocator Work
Clang VS Gcc - Optimization Including Operator New
If a 32-Bit Integer Overflows, How to Use a 40-Bit Structure Instead of a 64-Bit Long One
What's the Deal with Boost.Asio and File I/O
What Are Consequences of Forcing Qobject as a Parent of Qwidget
Why Does Std::Stack Use Std::Deque by Default
Int Var = 1; Void Main() { Int I = I; }
Throwing the Fattest People Off of an Overloaded Airplane
Combining Two Lists by Key Using Thrust
Can the Hwnd from Createwindow/Createdialog Be Getmessage'D from Another Thread
C++11: Why Does Std::Condition_Variable Use Std::Unique_Lock