patterncppModerate
Dynamically call lambda based on stream input
Viewed 0 times
streamdynamicallyinputcallbasedlambda
Problem
Some context: I have code that looks like this (minor issue noted here):
Anyway this connects to the MySQL DB and starts pulling data from the server. So inside execute I loop over the results and call the lambda for each row:
So here row gets a single row from the socket connection with mysql (so it calls the lambda as it receives each row (no pulling the rows into memory first)). So the code I want to review is pulling the data and calling the lambda.
This utilizes the helper class
Then the
```
// Caller::call()
// Reads the next argument required by the lambda from the stream.
// An exception will be generated if the next argument on the stream
// does not match the type expected by the lambda.
template
struct Caller
{
static void call(Action action, st
Statement select("SELECT * FROM People WHERE ID > ? AND ID < ?");
select.execute(1462, 1477, [](int ID, std::string const& person, double item1, float item2){
std::cout << "Got Row:"
<< ID << ", "
<< person << ", "
<< item1 << ", "
<< item2 << "\n";
});Anyway this connects to the MySQL DB and starts pulling data from the server. So inside execute I loop over the results and call the lambda for each row:
template
void execute(Args... param, Action action)
{
// STUFF TO SET up connection.
// Start retrieving rows.
while(row = results->getNextRow())
{
call(action, row);
}
}So here row gets a single row from the socket connection with mysql (so it calls the lambda as it receives each row (no pulling the rows into memory first)). So the code I want to review is pulling the data and calling the lambda.
// Statement::call
template
void call(Action action, std::unique_ptr& row)
{
typedef CallerTraits trait;
typedef typename trait::AllArgs AllArgs;
Caller::call(action, row);
}This utilizes the helper class
CallerTraits and Caller to pull the required rows from the stream and then call the lambda:// CallerTraits
// Get information about the arguments in the lambda
template
struct CallerTraits
: public CallerTraits
{};
template
struct CallerTraits
{
static const int size = sizeof...(Args);
typedef std::tuple AllArgs;
};Then the
Caller: ```
// Caller::call()
// Reads the next argument required by the lambda from the stream.
// An exception will be generated if the next argument on the stream
// does not match the type expected by the lambda.
template
struct Caller
{
static void call(Action action, st
Solution
I would have probably applied the following changes:
-
Wherever a function simply passes variadic arguments whose types have been deduced, I would have passed
It's a bit hard and quite long to explain how it works exactly - you can find a great explanation in the answer linked above -, but the main point is that using this particular recipe implements perfect forwarding:
The type of the parameters of
-
Instead of creating functions that take a
- Make
sizeastatic constexprvariable inCallerTraitsinstead of simplystatic const.
-
Wherever a function simply passes variadic arguments whose types have been deduced, I would have passed
args by universal reference (now officially called forwarding reference) and used std::forward to forward the results to the following functions:template
void doCall(Action action, std::unique_ptr& row, Args&&... args)
{
Caller::call(action, row, std::forward(args)...);
}It's a bit hard and quite long to explain how it works exactly - you can find a great explanation in the answer linked above -, but the main point is that using this particular recipe implements perfect forwarding:
template
void foo(X&& arg)
{
bar(std::forward(arg));
}The type of the parameters of
X&&... in foo will have the same const and reference qualifications than the type of the corresponding parameters in bar. Anyway, the link is by far clearer than I am. Simply remember the recipe and that for this recipe to work, the type X has to be deduced by the function; it may not work if X is known from somewhere else.-
Instead of creating functions that take a
std::unique_ptr& parameters, I would have had Caller::call and doCall them take a ResultSetRow& and dereferenced row right away. I don't know what is the exact return type of results->getNextRow() so I won't try to assume anything about it and the type that the main call should take as a parameter.Code Snippets
template<int size, int index, typename ArgumentTupple, typename Action, typename ...Args>
void doCall(Action action, std::unique_ptr<ResultSetRow>& row, Args&&... args)
{
Caller<size, index, ArgumentTupple, Action, Args...>::call(action, row, std::forward<Args>(args)...);
}template<typename X>
void foo(X&& arg)
{
bar(std::forward<X>(arg));
}Context
StackExchange Code Review Q#45310, answer score: 18
Revisions (0)
No revisions yet.