I'm replacing...about 6000 lines of deserialization C code with what I hope is about 3000 lines of C++. The basic existing pattern is like so:
ser_ret_t msgpack_unpack_mystruct(msgpack_object *obj, mystruct_t *d, void *unused = NULL)
{
NEXT_WORK_OBJ(array, i, work_obj);
ret |= msgpack_unpack_line(work_obj, &d->line);
NEXT_WORK_OBJ(array, i, work_obj);
ret |= msgpack_unpack_node(work_obj, &d->leftNode);
//etc....
This is repeated once for each variable within a given struct. array
, i
, and work_obj
are all state variables used by msgpack, and d
is an arbitrary structure pointer. NEXT_WORK_OBJ
is a preprocessor macro that is needed to adjust the state variables. msgpack_unpack_*
is a function with arbitrary parameters.
This is not only verbose, but incomplete, as the underlying buffer continues to be processed even in the case of a failure.
My thought is to replace the state variables with a state/helper class, with the one issue being calling an arbitrary function within the class. I'm handling that by not making the call within the class, but via lambda.
class UnpackHelper
{
public:
void GetNext(std::function<ser_ret_t(msgpack_object*)> callback)
{
//make sure it's safe to continue processing buffer.
if (m_status != SER_SUCCESS)
return;
NextObj();
m_status |= callback(m_pWork);
}
};
NextObj()
replaces the preprocessor macro, and adjusts the class internal state variables. Now the client code looks like so:
ser_ret_t msgpack_unpack_mystruct(msgpack_object *obj, mystruct_t *d, void *unused = NULL)
{
UnpackHelper up;
up.GetNext([=](auto pWork) {return msgpack_unpack_line(pWork, &d->line); });
up.GetNext([=](auto pWork) {return msgpack_unpack_node(pWork, &d->leftNode);});
My question is specifically about the client code, and the lambdas. My understanding is that a default capture
is happening here, and I further understand that those are not recommended. Because the capture is always going to happen on a member of a pointer passed into the function (d
), though, I believe this pattern is safe. Is this correct? Given the thousands of lambdas that will exist, I would like to avoid unnecessary verbosity. Second, is it possible to shorten or remove the parameter list in any way? (auto pWork)
is fairly minimal, but...
My new, third question, based on learning about Looks like this could work, but buys me nothing but code bloat caused by template expansion.std::function
..is my callback mechanism even necessary? std::function
looks like something I might be able to use to create an arbitrary call directly within the class. I'm currently looking at the accepted answer here: C++: pass function with arbitrary number of parameters as a parameter to see if it can be applied to my code...without using C-style variadic args.