Can someone tell me how to remove the repeated specializations below?
#include <iostream>
#include <fstream>
#include <string>
struct Thing {
int a, b;
void load (std::istream& is) {is >> std::skipws >> a >> b;}
};
struct Object {
int a, b, c;
void load (std::istream& is) {is >> std::skipws >> a >> b >> c;}
};
template <typename...> struct PassArgs;
// General case.
template <typename First, typename... Rest>
struct PassArgs<First, Rest...> : PassArgs<Rest...> {
void operator()(std::istream& is, First& first, Rest&... rest) const {
is >> first;
PassArgs<Rest...>::operator()(is, rest...);
}
};
// Specialization for std::string needed.
template <typename... Rest>
struct PassArgs<std::string, Rest...> : PassArgs<Rest...> {
void operator()(std::istream& is, std::string& first, Rest&... rest) const {
while (std::getline (is, first) && first.empty());
PassArgs<Rest...>::operator()(is, rest...);
}
};
// Specialization for class Thing.
template <typename... Rest>
struct PassArgs<Thing, Rest...> : PassArgs<Rest...> {
void operator()(std::istream& is, Thing& first, Rest&... rest) const {
first.load(is);
PassArgs<Rest...>::operator()(is, rest...);
}
};
// Specialization for class Object, but is the exact same as that for Thing.
template <typename... Rest>
struct PassArgs<Object, Rest...> : PassArgs<Rest...> {
void operator()(std::istream& is, Object& first, Rest&... rest) const {
first.load(is);
PassArgs<Rest...>::operator()(is, rest...);
}
};
template <>
struct PassArgs<> {
void operator()(std::istream&) const {} // End of recursion.
};
int main() {}
Everything works correctly, but is there a way to avoid specializations for all classes that have the load(std::istream&)
function (and there are many in my program). Currently, I have specializations for Thing
, Object
, and many other classes which all have the same lines in their specializations.
Incidentally, this is how the client uses PassArgs
:
template <typename T, typename... Args>
T* create (std::istream& is, Args&... args) {
PassArgs<Args...>()(is, args...);
T* t = new T(args...);
// Do whatever with t;
return t;
}
Define a trait to detect the
load
member:Hoist the actual loading for a single type into a separate class template, specialized for types with a
load
member (using the trait):Then use that in your main template:
The
string
case can be done by specializingPassArg
too.N.B. I made the new class have a static function called
pass
notoperator()
, because if you find yourself writing this:or worse, calling
operator()
by name like this:Then you probably don't want a functor.
PassArgs
is stateless, so there's no point creating an instance of it, and if you have to nameoperator()
explicitly then you're doing it wrong. Give the function a proper name and call that, and make it static: