I have to work with a couple of legacy C-functions that as usual, have input, input-output, and output parameters and I want to wrap them in a C++ function.
The C function looks like this:
void Cfun(double* in_a, double* io_b, double* out_c, double* in_destr_d){}
In the documentation it says that the first parameter a
is an input parameter, b
is input-output, c
is output and d
is a an input parameter that on return will contain an unspecified (garbage) value. (and all the pointers must be non-null.)
In C++ is obvious that one can refactor Cfun
without loss in the following way:
double fun(double const& a, double& b, double& d){
double c;
Cfun(const_cast<double*>(&a), &b, &c, &d);
return c;
}
That is, a
is promoted to a const reference, b
and d
are promoted to a reference and c
is promoted to a return value.
This function can be used as this:
double a = 1.1;
double b = 1.2;
double d = 1.3;
i)
...
double c1 = fun(1.1, b, d);
ii)
...
double c1 = fun(a , b, d);
// b has useful information here
// d has garbage
That is the second and third argument must be l-values, which is fine.
However there is a subtle difference between the parameters b
and d
. While b
has useful information d
is essentially destroyed and using its value after the function call would be an error in any case.
This situation reminded me of r-value reference and move in C++11. Where moved r-value parameters end up being in unspecified but assignable states. This makes me think that an input-to-garbage parameter in C can be more or less mapped to r-value reference.
In other words, the wrapper can be written in this way to convey that the value of d
, not only will change but it will be left in an unusable state.
double fun2(double const& a, double& b, double&& d){
double c;
Cfun(const_cast<double*>(&a), &b, &c, &d);
return c;
}
Does this sound reasonable, am I lossing something by defining this wrapper? Is something like this practiced in a similar context? Does this justify an r-value to a built-in type (like double
here)?
A slight problem I find is that the syntax will become more verbose, albeit more clear from the point of view of the usability of d
after the function call.
As far as I can see now the possible syntaxes change to:
i) ... // double c = fun2(1.1, b, d); // now compile error, d is not a r-value double c = fun2(1.1, b, std::move(d));
// obvious error to use the value of d, since it was (probably) moved. However "moving a POD??" d = 2.; // assignment is ok
ii)
...
double c = fun2(1.1, b, 1.3); // good, I can pass a literal now, nothing to misuse
iii)
...
double c = fun2(1.1, b, double{d}); // not clear, reaction "this seems a redudant cast"
iv)
...
double c = fun2(1.1, b, copy(d)); // verbose, but "d should be fine because it was copied"
where template<class T> T copy(T&& t){return t;}
In summary, is fun2
a better wrapper to Cfun
than fun
?
Aucun commentaire:
Enregistrer un commentaire