Date: Sun, 13 Jun 2021 09:19:57 -0700
I understand that adding any parameters after a parameter pack expansion disables deduction for the parameter pack.
At this distance after the introduction of parameter packs I’m finding it difficult to find the pre-standardization discussion papers which I think would help me understand why this is - can anyone link me to those?
Background/motivation:
TLDR: I’d like this to allow deduction of Args here:
template<typename Args…>
void foo(Args&&…args, source_location callerLocation = source_location::current())
TL version:
I started to care when source_location was added to the standard: I can get program-call-structure backtracing on exceptions with something like this:
// Function declarations
void foo(/* Whatever other parameters */, source_location callerLocation = source_location::current());
// Function definitions
void foo(/* Whatever other parameters */, source_location callerLocation) {
try {
// Code
} // catch anything we want to handle here
catch (…) {
exceptionHandler(callerLocation);
}
}
Function `exceptionHandler ` processes `current_exception()`: if it’s already my custom exception type that includes the list of caller locations it pushes the new caller location onto the end of that list and rethrows it, otherwise it wraps the exception_ptr (so preserving the original type information) in that type, puts the caller location as the only entry in the list and then throws that.
This works for every function, unless /* Whatever other parameters */ is a parameter pack, because it breaks deduction.
In synchronous code I get a subset of the information I would have from getting the backtrace at throw-time from a debugger. It has the disadvantage of being less complete, because of any calls that do not implement this protocol, but has the significant advantage of being entirely standard C++ and therefore working the same way on every system.
When you get to asynchronous coroutine code, the debugger’s backtrace is almost completely useless for bugs in your code’s control flow because everything looks something like yourFrame -> someExecutor -> std::thread::run. This technique gives me the chain of calls that got me to that place across any number of suspend/resume events.
Alternative way to solve this problem:
If source_location could be specified to be a valid non-type template parameter type (currently isn’t AFAICT) and the rules changed in such a way that a default value of source_location::current expanded to the expansion location (currently it’s the template declaration location AFAICT) then I could write:
template<typename…Args, source_location callerLocation = source_location::current()>
void foo(Args&&…args);
Thanks,
Phil
At this distance after the introduction of parameter packs I’m finding it difficult to find the pre-standardization discussion papers which I think would help me understand why this is - can anyone link me to those?
Background/motivation:
TLDR: I’d like this to allow deduction of Args here:
template<typename Args…>
void foo(Args&&…args, source_location callerLocation = source_location::current())
TL version:
I started to care when source_location was added to the standard: I can get program-call-structure backtracing on exceptions with something like this:
// Function declarations
void foo(/* Whatever other parameters */, source_location callerLocation = source_location::current());
// Function definitions
void foo(/* Whatever other parameters */, source_location callerLocation) {
try {
// Code
} // catch anything we want to handle here
catch (…) {
exceptionHandler(callerLocation);
}
}
Function `exceptionHandler ` processes `current_exception()`: if it’s already my custom exception type that includes the list of caller locations it pushes the new caller location onto the end of that list and rethrows it, otherwise it wraps the exception_ptr (so preserving the original type information) in that type, puts the caller location as the only entry in the list and then throws that.
This works for every function, unless /* Whatever other parameters */ is a parameter pack, because it breaks deduction.
In synchronous code I get a subset of the information I would have from getting the backtrace at throw-time from a debugger. It has the disadvantage of being less complete, because of any calls that do not implement this protocol, but has the significant advantage of being entirely standard C++ and therefore working the same way on every system.
When you get to asynchronous coroutine code, the debugger’s backtrace is almost completely useless for bugs in your code’s control flow because everything looks something like yourFrame -> someExecutor -> std::thread::run. This technique gives me the chain of calls that got me to that place across any number of suspend/resume events.
Alternative way to solve this problem:
If source_location could be specified to be a valid non-type template parameter type (currently isn’t AFAICT) and the rules changed in such a way that a default value of source_location::current expanded to the expansion location (currently it’s the template declaration location AFAICT) then I could write:
template<typename…Args, source_location callerLocation = source_location::current()>
void foo(Args&&…args);
Thanks,
Phil
Received on 2021-06-13 11:20:01