I want to achieve something like:
export_vars("path/to/file.dat", {"variable_name", obj}, {"another_variable", 2});
where obj
can be any type as long as it has an <<
overload - the idea is to write to an ofstream
later on. I have tried (for an initializer_list
of pairs):
void
export_vars(const std::string& path, std::initializer_list<std::pair<std::string, std::any>> args)
{
for (auto& [name, var] : args)
std::cout << name << ": " << var << std::endl;
}
but std::any
cannot be <<
without knowing the underlying type. Can it maybe be achieved using variadic templates and parameter pack expansion? I also tried something like:
template <class... Args>
void
export_vars(const std::string& path, Args... args)
{
(std::cout << ... << args.first << args.second) << std::endl;
}
but that's obviously wrong. Any suggestions?
{..}
has no type, and so disallows most deduction.
Several work arounds:
Change call to use std::pair
explicitly:
template <typename ... Pairs>
void export_vars(const std::string&, const Pairs&... args)
{
((std::cout << args.first << ": " << args.second << std::endl), ...);
}
int main()
{
export_vars("unused", std::pair{"int", 42}, std::pair{"cstring", "toto"});
}
Don't use template:
void export_vars(const std::string&,
const std::initializer_list<std::pair<std::string, Streamable>>& args)
{
for (const auto& [name, value] : args) {
std::cout << name << ": " << value << std::endl;
}
}
int main()
{
export_vars("unused", {{"int", 42}, {"cstring", "toto"}});
}
with Streamable
using type-erasure, possibly something like:
class Streamable
{
struct IStreamable
{
virtual ~IStreamable() = default;
virtual void print(std::ostream&) = 0;
};
template <typename T>
struct StreamableT : IStreamable
{
StreamableT(T t) : data(std::forward<T>(t)) {}
virtual void print(std::ostream& os) { os << data; }
T data;
};
std::unique_ptr<IStreamable> ptr;
public:
template <typename T>
// Possibly some concepts/SFINAE as requires(is_streamable<T>)
Streamable(T&& t) : ptr{std::make_unique<StreamableT<std::decay_t<T>>>(t)} {}
friend std::ostream& operator << (std::ostream& os, const Streamable& streamable)
{
streamable.ptr->print(os);
return os;
}
};