Create a class wrapper variable

I have a Reader template class

 template<typename T> class Reader{ typedef T type; }; 

Special implementations (derrived classes) have methods with a signature

T read(IStream&, any number of arguments, zero possible)

ie class IntegerReader public functions:

 template <typename T> class IntegerReader : public Reader<T>{ public: T read(IStream& stream); T read(IStream& stream, T min, T max); T read(IStream& stream, T min, T max, std::string name); } 

Now I want to create a wrapper that will allow me to create another reader, with a call to some reader with arguments.

I tried this:

 template <typename T, typename... Args> class ParametrizedReader : public Reader<typename T::type> { T reader; Args... args; ParametrizedReader(T reader, Args... args):reader(reader), args(args){ } typename T::type read(IStream& stream){ return reader.read(args..., stream); } }; testlib/readerWrapper.hpp:7:6: error: expected unqualified-id before '...' token testlib/readerWrapper.hpp: In constructor 'ParametrizedReader<T, Args>::ParametrizedReader(T, Args ...)': testlib/readerWrapper.hpp:8:61: error: class 'ParametrizedReader<T, Args>' does not have any field named 'args' testlib/readerWrapper.hpp: In member function 'typename T::type ParametrizedReader<T, Args>::read(IStream&)': testlib/readerWrapper.hpp:12:22: error: 'args' was not declared in this scope 

 template <typename T, typename... Args> class ParametrizedReader : public Reader<typename T::type> { std::function<T()> lambda; ParametrizedReader(T reader, Args... args){ lambda = [=](IStream& stream){ reader.read(stream, args...); }; } typename T::type read(IStream& stream){ return lambda(stream); } }; testlib/readerWrapper.hpp:9:24: error: parameter packs not expanded with '...': testlib/readerWrapper.hpp:9:24: note: 'args' testlib/readerWrapper.hpp:9:28: error: expansion pattern 'args' contains no argument packs 

and this:

 template <typename T, typename... Args> class ParametrizedReader : public Reader<typename T::type> { std::function<T()> lambda; ParametrizedReader(T reader, Args... args){ lambda = [reader, args...](IStream& stream){ reader.read(stream, args...); }; } typename T::type read(IStream& stream){ return lambda(stream); } }; testlib/readerWrapper.hpp:8:25: error: expected ',' before '...' token testlib/readerWrapper.hpp:8:25: error: expected identifier before '...' token testlib/readerWrapper.hpp:8:28: error: parameter packs not expanded with '...': testlib/readerWrapper.hpp:8:28: note: 'args' 

G ++ 4.7 compilation errors

Although I am not sure that the first example is correct and should be compiled, I believe that the second and third should.

I found this error , which does not seem to be fixed.

Are there any workarounds, how can I do what I want?

+4
source share
3 answers

You can get around this problem by attaching the arguments to the lambda and not capturing them.

 ParametrizedReader(T reader, Args... args){ lambda = std::bind( [=](IStream& stream, Args... as){ reader.read(stream, as...); }, args...); } 

Although you might want to do what @Alexandre says and does not parameterize the class template for exact argument types:

 template <typename T> class ParametrizedReader : public Reader<typename T::type> { std::function<T()> lambda; template<typename... Args> ParametrizedReader(T reader, Args... args){ lambda = std::bind( [=](IStream& stream, Args... as){ reader.read(stream, as...); }, args...); } // ... }; 

(Note: not completed.)

What may also work is simply to abandon std::bind in the second fragment and try both the second and third solutions, this time with the template of the variational function. Maybe it works, who knows.

+4
source

This reminds me of a known bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41933 It affects at least gcc 4.6.2 and 4.7.x.

Clang 3.1 has no problems with variations in lambda captures.

Perhaps you can follow the old-fashioned route and save the material in a tuple: http://liveworkspace.org/code/7d4347021aaf004489591e78654f0233

 #include <tuple> #include <vector> #include <string> //////////////////////////////////// template<int ...> struct seq { }; template<int N, int ...S> struct gens : gens<N-1, N-1, S...> { }; template<int ...S> struct gens<0, S...> { typedef seq<S...> type; }; //////////////////////////////////// template<typename T> struct Reader { typedef T type; }; //Special implementations (derrived classes) have methods with signature struct IStream {}; template <typename T> class IntegerReader : public Reader<T> { public: T read(IStream& stream); T read(IStream& stream, T min, T max); T read(IStream& stream, T min, T max, std::string name); }; template <typename T, typename... Args> class ParametrizedReader : public Reader<typename T::type> { T _reader; std::tuple<Args...> _args; public: ParametrizedReader(T reader, Args... args) : _reader(reader), _args(std::forward<Args>(args)...) { } typename T::type read(IStream& stream) { callFunc(typename gens<sizeof...(Args)>::type()); } template<int ...S> void callFunc(seq<S...>) { func(std::get<S>(_args) ...); } }; template <typename T, typename... Args> ParametrizedReader<T, Args...> make_parameterized_reader(T reader, Args... args) { return ParametrizedReader<T, Args...>(reader, std::forward<Args>(args)...); } int main(int argc, const char *argv[]) { Reader<char> reader; auto pr = make_parameterized_reader(reader, "stuff", 3.14, std::string("you can think of"), std::vector<int> { 1,2,3 }); } 
+3
source

I would drop the package of parameters from the class template, use excellent redirects and std :: bind.

 template <typename T> class ParametrizedReader : public Reader<typename T::type> { std::function<T()> lambda; template <typename... Args> ParametrizedReader(T reader, Args&&... args) { using namespace std::placeholders; lambda = std::bind( std::mem_fn(&Reader::read), _1, std::forward<Args>(args)...); } typename T::type read(IStream& stream) { return lambda(stream); } }; 
0
source

Source: https://habr.com/ru/post/1438086/


All Articles