Reputation: 409
I've developed a couple of generic reader and writer wrapper functions (mostly to avoid having to handle exceptions on each part of the code that reads or writes a file). My functions have these signatures:
For writing:
/** Generic writer function alias for free functions */
template<typename ... Args>
using WriterFunction = void (*)(std::ofstream &, Args const & ... args);
/**
* \brief Wrapper used to encapsulate a generic free function writing to an output stream
* \param[in] outFileName Name of the output file to be written.
* \param[in] writeFunction Function used to export the data.
* \param[in] ... Variable number of arguments to be passed to the previous function.
*/
template<typename ... Args>
void writerFunctionWrapper(
std::string const &outFileName,
WriterFunction<Args ...> writeFunction,
Args const & ... args);
and for reading:
/** Generic reader function alias for free functions */
template<typename ... Args>
using ReaderFunction = void (*)(std::ifstream &, Args & ... args);
/**
* \brief Wrapper used to encapsulate a generic free function reading from an input stream
* \param[in] inFileName Name of the input file to be read.
* \param[in] readFunction Function used to import the data.
* \param[in] ... Variable number of arguments to be passed to the previous function.
*/
template<typename ... Args>
void readerFunctionWrapper(
std::string const &inFileName,
ReaderFunction<Args ...> readFunction,
Args & ... args);
So, the main difference is that the writer considers the input parameters as const
, as I expect to read from them into the std::ostream
, while viceversa, the reader considers the input parameters as non-const
, as I expect to write to them from the std::istream
.
This works fine, but I've come to a point where I'd like to have reader functions with const
parameters in their variable list, or writer functions with non-const
parameters as well. However, I can't figure out how to do that when the list of parameters is variable.
I tried modifying my alias and templates to
template<typename ... ArgsConst, typename ... ArgsNonConst>
using WriterFunction = void (*)(std::ofstream &, ArgsConst const & ... argsConst, ArgsNonConst & ... argsNonConst);
template<
typename ... ArgsConst,
typename ... ArgsNonConst>
void writerFunctionWrapper(
std::string const &outFileName,
WriterFunction<ArgsConst ..., ArgsNonConst ...> writeFunction,
ArgsConst const & ... argsConst,
ArgsNonConst & ... argsNonConst);
which has the downside to force an order in the way arguments are placed in the writeFunction()
, first the const
, then the non-const
, but this is a lesser evil. However, that doesn't work for me:
/home/code/Utils/include/file_utils.h:42:11: error: parameter pack ‘ArgsConst’ must be at the end of the template parameter list
template<typename ... ArgsConst, typename ... ArgsNonConst>
I also tried simply mixing const
and non-const
parameters by extending my alias and template as:
/** Generic writer function alias for free functions */
template<typename ... Args>
using WriterFunctionGeneral = void (*)(std::ofstream &, Args const & ... argsConst, Args & ... argsNonConst);
template<typename ... Args>
void writerFunctionWrapper(
std::string const &outFileName,
WriterFunctionGeneral<Args ...> writeFunctionGeneral,
Args const & ... argsConst,
Args & ... argsNonConst);
But that didn't work either when I tried to use it on a function mixing const
and non-const
parameters, as:
void writeSomeEntity(
std::ofstream &outWriter,
ConstOutVar const &constVar,
NonConstOutVar &nonConstVar);
And invoke it as:
ConstOutVar constVar;
NonConstOutVar nonConstVar;
...
Utils::writerFunctionWrapper(outFileName, &writeSomeEntity, constVar, nonConstVar);
but that didn't work either:
/home/code/OutputWriter/src/entity_writer.cpp:46:122: error: no matching function for call to ‘writerFunctionWrapper(std::string&, void (*)(std::ofstream&, const ConstOutVar &, NonConstOutVar &), ConstOutVar &, NonConstOutVar &)’
Utils::writerFunctionWrapper(outFileName, writeSomeEntity, constOutVar, nonConstOutVar);
(Notice how the error shows constOutVar & instead of constOutVar const &)
I noticed [this question] (Variadic template trouble matching const and non-const std::string), but that's not really the situation I'm describing here.
Any suggestions?
Thanks a lot in advanced.
Upvotes: 1
Views: 628
Reputation: 66210
Any suggestions?
Just three.
(1) Accept the function in your wrapper as a generic template type, ignoring the exact type of the arguments received; this way your wrapper is extremely more flexible because can accept also a std::function
or a lambda (also a generic lambda) or (almost the same) a struct/class with an operator()
(maybe a template one). This is impossible is you accept only function pointers
(2) receive the arguments as universal references forwarding references and use perfect forwarding; perfect forwarding is designed exactly to solve to problem you're facing.
Following suggestions (1) and (2), you needs a single wrapper (why do you needs a reader and a writer wrapper if both of they accept input only and input/output arguments?) and the following could be a simplified example
template <typename F, typename ... Args>
void FunctionWrapper(F && f, Args && ... args)
{ std::forward<F>(f)(std::forward<Args>(args)...); }
But if you really really (really!) want one or more wrappers accepting only traditional function pointers, here come suggestion (3)
(3) use two variadic sets of template types for arguments; one set for the function pointer and one set for arguments; I mean... something as follows
template <typename ... FAs, typename ... As>
void FunctionWrapperPnt (void(*f)(FAs...), As && ... as)
{ f(std::forward<As>(as)...); }
This is important to avoid that you can pass a function that receive (by example) a std::string
and an argument that is a char const []
(a string literal) giving an error in deducing types.
Upvotes: 3