Using fields stored in variadic data structure as method arguments
I have a variadic data structure, each "layer" containing one field.
How can use all the fields stored in the structure as arguments to a function or a constructor?
template <class... Ts> class Builder {};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// want to use tail, Builder<Ts...>::tail, etc.
// as ctor or function arguments without multiple specializations
}
private:
const T tail;
};
In general, I want to be capable of doing something like this:
Builder<int, string, int> b1{10, "aaa", 20};
Result r1 = b1.build(); // should invoke Result's constructor (int, string, int)
Builder<int> b2{10};
Result r2 = b2.build(); // should invoke Result's constructor (int)
c++ c++11 recursion variadic-templates template-meta-programming
add a comment |
I have a variadic data structure, each "layer" containing one field.
How can use all the fields stored in the structure as arguments to a function or a constructor?
template <class... Ts> class Builder {};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// want to use tail, Builder<Ts...>::tail, etc.
// as ctor or function arguments without multiple specializations
}
private:
const T tail;
};
In general, I want to be capable of doing something like this:
Builder<int, string, int> b1{10, "aaa", 20};
Result r1 = b1.build(); // should invoke Result's constructor (int, string, int)
Builder<int> b2{10};
Result r2 = b2.build(); // should invoke Result's constructor (int)
c++ c++11 recursion variadic-templates template-meta-programming
1
And why not just hold astd::tupleand use the many standard library utilities to make ones life easier?
– StoryTeller
Nov 14 '18 at 14:28
The builder is unfortunately only a simplification, in real scenario it's a composite buffer composed of multiple sub-buffers that return the value (the fieldtaildoes not exist).
– Adam Kotwasinski
Nov 14 '18 at 14:34
2
The solution utilizes thetailfield. So if the example is not indicative, and the solution is not applicable, what was accomplished here?
– StoryTeller
Nov 14 '18 at 14:35
add a comment |
I have a variadic data structure, each "layer" containing one field.
How can use all the fields stored in the structure as arguments to a function or a constructor?
template <class... Ts> class Builder {};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// want to use tail, Builder<Ts...>::tail, etc.
// as ctor or function arguments without multiple specializations
}
private:
const T tail;
};
In general, I want to be capable of doing something like this:
Builder<int, string, int> b1{10, "aaa", 20};
Result r1 = b1.build(); // should invoke Result's constructor (int, string, int)
Builder<int> b2{10};
Result r2 = b2.build(); // should invoke Result's constructor (int)
c++ c++11 recursion variadic-templates template-meta-programming
I have a variadic data structure, each "layer" containing one field.
How can use all the fields stored in the structure as arguments to a function or a constructor?
template <class... Ts> class Builder {};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// want to use tail, Builder<Ts...>::tail, etc.
// as ctor or function arguments without multiple specializations
}
private:
const T tail;
};
In general, I want to be capable of doing something like this:
Builder<int, string, int> b1{10, "aaa", 20};
Result r1 = b1.build(); // should invoke Result's constructor (int, string, int)
Builder<int> b2{10};
Result r2 = b2.build(); // should invoke Result's constructor (int)
c++ c++11 recursion variadic-templates template-meta-programming
c++ c++11 recursion variadic-templates template-meta-programming
edited Nov 14 '18 at 14:38
max66
35.5k73964
35.5k73964
asked Nov 14 '18 at 14:25
Adam KotwasinskiAdam Kotwasinski
2,538827
2,538827
1
And why not just hold astd::tupleand use the many standard library utilities to make ones life easier?
– StoryTeller
Nov 14 '18 at 14:28
The builder is unfortunately only a simplification, in real scenario it's a composite buffer composed of multiple sub-buffers that return the value (the fieldtaildoes not exist).
– Adam Kotwasinski
Nov 14 '18 at 14:34
2
The solution utilizes thetailfield. So if the example is not indicative, and the solution is not applicable, what was accomplished here?
– StoryTeller
Nov 14 '18 at 14:35
add a comment |
1
And why not just hold astd::tupleand use the many standard library utilities to make ones life easier?
– StoryTeller
Nov 14 '18 at 14:28
The builder is unfortunately only a simplification, in real scenario it's a composite buffer composed of multiple sub-buffers that return the value (the fieldtaildoes not exist).
– Adam Kotwasinski
Nov 14 '18 at 14:34
2
The solution utilizes thetailfield. So if the example is not indicative, and the solution is not applicable, what was accomplished here?
– StoryTeller
Nov 14 '18 at 14:35
1
1
And why not just hold a
std::tuple and use the many standard library utilities to make ones life easier?– StoryTeller
Nov 14 '18 at 14:28
And why not just hold a
std::tuple and use the many standard library utilities to make ones life easier?– StoryTeller
Nov 14 '18 at 14:28
The builder is unfortunately only a simplification, in real scenario it's a composite buffer composed of multiple sub-buffers that return the value (the field
tail does not exist).– Adam Kotwasinski
Nov 14 '18 at 14:34
The builder is unfortunately only a simplification, in real scenario it's a composite buffer composed of multiple sub-buffers that return the value (the field
tail does not exist).– Adam Kotwasinski
Nov 14 '18 at 14:34
2
2
The solution utilizes the
tail field. So if the example is not indicative, and the solution is not applicable, what was accomplished here?– StoryTeller
Nov 14 '18 at 14:35
The solution utilizes the
tail field. So if the example is not indicative, and the solution is not applicable, what was accomplished here?– StoryTeller
Nov 14 '18 at 14:35
add a comment |
5 Answers
5
active
oldest
votes
If you don't want to use tuple as a member to hold the values, you could do it this way:
template <class... Ts> class Builder {
protected:
template<class...Us>
Result do_build(const Us&...us){
return Result(us...);
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
return do_build();
}
protected:
template<class...Us>
Result do_build(const Us&...us){
return Builder<Ts...>::do_build(us...,tail);
}
private:
const T tail;
};
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
add a comment |
template <class... Ts>struct Builder {
auto as_tie() const { return std::tie(); }
};
template <class T, class... Ts>
struct Builder<T, Ts...> : Builder<Ts...> {
using base = Builder<Ts...>;
auto as_tie()const{
return std::tuple_cat( base::as_tie(), std::tie( tail ) );
}
now Builder::as_tie() can be passed to std::apply (or backported version) or make_from_tuple.
Naturally the operator T trick can be used for return type deduction. But I'd usually advise against it.
add a comment |
You can use an Idx<n> tag to get tail from n-th Builder:
template<std::size_t i> struct Idx {};
template<class... Ts>
class Builder {
public:
void get_tail();
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
static constexpr auto index = sizeof...(Ts);
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
}
Result build() {
return build_impl(std::make_index_sequence<index + 1>{});
}
protected:
using Builder<Ts...>::get_tail;
const T& get_tail(Idx<index>) {
return tail;
}
private:
template<std::size_t... is>
Result build_impl(std::index_sequence<is...>) {
return Result{get_tail(Idx<index - is>{})...};
}
private:
const T tail;
};
add a comment |
I suppose you can use a lambda (and save it in a std::function) to stock values.
Something as (caution: code not tested) (thanks to Oliv for a correction)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
1
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
@Oliv - sure:Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one constructBuilderwith a value that is destroyed before thebuild()calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.
– max66
Nov 14 '18 at 15:10
1
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.[=]
– Oliv
Nov 14 '18 at 19:31
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
add a comment |
One of the solutions I found is to pass intermediary tuple<...> that contains the fields and then unpack it using the mechanism described in "unpacking" a tuple to call a matching function pointer :
// unpacking helpers
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;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
Then it behaves properly:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
Still, maybe it's possible to do something simpler without that tuple intermediary?
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53302451%2fusing-fields-stored-in-variadic-data-structure-as-method-arguments%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
If you don't want to use tuple as a member to hold the values, you could do it this way:
template <class... Ts> class Builder {
protected:
template<class...Us>
Result do_build(const Us&...us){
return Result(us...);
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
return do_build();
}
protected:
template<class...Us>
Result do_build(const Us&...us){
return Builder<Ts...>::do_build(us...,tail);
}
private:
const T tail;
};
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
add a comment |
If you don't want to use tuple as a member to hold the values, you could do it this way:
template <class... Ts> class Builder {
protected:
template<class...Us>
Result do_build(const Us&...us){
return Result(us...);
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
return do_build();
}
protected:
template<class...Us>
Result do_build(const Us&...us){
return Builder<Ts...>::do_build(us...,tail);
}
private:
const T tail;
};
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
add a comment |
If you don't want to use tuple as a member to hold the values, you could do it this way:
template <class... Ts> class Builder {
protected:
template<class...Us>
Result do_build(const Us&...us){
return Result(us...);
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
return do_build();
}
protected:
template<class...Us>
Result do_build(const Us&...us){
return Builder<Ts...>::do_build(us...,tail);
}
private:
const T tail;
};
If you don't want to use tuple as a member to hold the values, you could do it this way:
template <class... Ts> class Builder {
protected:
template<class...Us>
Result do_build(const Us&...us){
return Result(us...);
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
return do_build();
}
protected:
template<class...Us>
Result do_build(const Us&...us){
return Builder<Ts...>::do_build(us...,tail);
}
private:
const T tail;
};
answered Nov 14 '18 at 14:38
OlivOliv
9,0491957
9,0491957
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
add a comment |
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
How would build() work? Can you elaborate?
– rustyx
Nov 14 '18 at 20:26
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
@rustyx this way action: godbolt.org/z/DCT3L6
– Oliv
Nov 14 '18 at 20:43
add a comment |
template <class... Ts>struct Builder {
auto as_tie() const { return std::tie(); }
};
template <class T, class... Ts>
struct Builder<T, Ts...> : Builder<Ts...> {
using base = Builder<Ts...>;
auto as_tie()const{
return std::tuple_cat( base::as_tie(), std::tie( tail ) );
}
now Builder::as_tie() can be passed to std::apply (or backported version) or make_from_tuple.
Naturally the operator T trick can be used for return type deduction. But I'd usually advise against it.
add a comment |
template <class... Ts>struct Builder {
auto as_tie() const { return std::tie(); }
};
template <class T, class... Ts>
struct Builder<T, Ts...> : Builder<Ts...> {
using base = Builder<Ts...>;
auto as_tie()const{
return std::tuple_cat( base::as_tie(), std::tie( tail ) );
}
now Builder::as_tie() can be passed to std::apply (or backported version) or make_from_tuple.
Naturally the operator T trick can be used for return type deduction. But I'd usually advise against it.
add a comment |
template <class... Ts>struct Builder {
auto as_tie() const { return std::tie(); }
};
template <class T, class... Ts>
struct Builder<T, Ts...> : Builder<Ts...> {
using base = Builder<Ts...>;
auto as_tie()const{
return std::tuple_cat( base::as_tie(), std::tie( tail ) );
}
now Builder::as_tie() can be passed to std::apply (or backported version) or make_from_tuple.
Naturally the operator T trick can be used for return type deduction. But I'd usually advise against it.
template <class... Ts>struct Builder {
auto as_tie() const { return std::tie(); }
};
template <class T, class... Ts>
struct Builder<T, Ts...> : Builder<Ts...> {
using base = Builder<Ts...>;
auto as_tie()const{
return std::tuple_cat( base::as_tie(), std::tie( tail ) );
}
now Builder::as_tie() can be passed to std::apply (or backported version) or make_from_tuple.
Naturally the operator T trick can be used for return type deduction. But I'd usually advise against it.
answered Nov 14 '18 at 14:49
Yakk - Adam NevraumontYakk - Adam Nevraumont
184k19191376
184k19191376
add a comment |
add a comment |
You can use an Idx<n> tag to get tail from n-th Builder:
template<std::size_t i> struct Idx {};
template<class... Ts>
class Builder {
public:
void get_tail();
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
static constexpr auto index = sizeof...(Ts);
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
}
Result build() {
return build_impl(std::make_index_sequence<index + 1>{});
}
protected:
using Builder<Ts...>::get_tail;
const T& get_tail(Idx<index>) {
return tail;
}
private:
template<std::size_t... is>
Result build_impl(std::index_sequence<is...>) {
return Result{get_tail(Idx<index - is>{})...};
}
private:
const T tail;
};
add a comment |
You can use an Idx<n> tag to get tail from n-th Builder:
template<std::size_t i> struct Idx {};
template<class... Ts>
class Builder {
public:
void get_tail();
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
static constexpr auto index = sizeof...(Ts);
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
}
Result build() {
return build_impl(std::make_index_sequence<index + 1>{});
}
protected:
using Builder<Ts...>::get_tail;
const T& get_tail(Idx<index>) {
return tail;
}
private:
template<std::size_t... is>
Result build_impl(std::index_sequence<is...>) {
return Result{get_tail(Idx<index - is>{})...};
}
private:
const T tail;
};
add a comment |
You can use an Idx<n> tag to get tail from n-th Builder:
template<std::size_t i> struct Idx {};
template<class... Ts>
class Builder {
public:
void get_tail();
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
static constexpr auto index = sizeof...(Ts);
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
}
Result build() {
return build_impl(std::make_index_sequence<index + 1>{});
}
protected:
using Builder<Ts...>::get_tail;
const T& get_tail(Idx<index>) {
return tail;
}
private:
template<std::size_t... is>
Result build_impl(std::index_sequence<is...>) {
return Result{get_tail(Idx<index - is>{})...};
}
private:
const T tail;
};
You can use an Idx<n> tag to get tail from n-th Builder:
template<std::size_t i> struct Idx {};
template<class... Ts>
class Builder {
public:
void get_tail();
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
private:
static constexpr auto index = sizeof...(Ts);
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {
}
Result build() {
return build_impl(std::make_index_sequence<index + 1>{});
}
protected:
using Builder<Ts...>::get_tail;
const T& get_tail(Idx<index>) {
return tail;
}
private:
template<std::size_t... is>
Result build_impl(std::index_sequence<is...>) {
return Result{get_tail(Idx<index - is>{})...};
}
private:
const T tail;
};
edited Nov 14 '18 at 15:20
answered Nov 14 '18 at 15:01
EvgEvg
3,81221334
3,81221334
add a comment |
add a comment |
I suppose you can use a lambda (and save it in a std::function) to stock values.
Something as (caution: code not tested) (thanks to Oliv for a correction)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
1
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
@Oliv - sure:Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one constructBuilderwith a value that is destroyed before thebuild()calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.
– max66
Nov 14 '18 at 15:10
1
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.[=]
– Oliv
Nov 14 '18 at 19:31
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
add a comment |
I suppose you can use a lambda (and save it in a std::function) to stock values.
Something as (caution: code not tested) (thanks to Oliv for a correction)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
1
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
@Oliv - sure:Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one constructBuilderwith a value that is destroyed before thebuild()calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.
– max66
Nov 14 '18 at 15:10
1
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.[=]
– Oliv
Nov 14 '18 at 19:31
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
add a comment |
I suppose you can use a lambda (and save it in a std::function) to stock values.
Something as (caution: code not tested) (thanks to Oliv for a correction)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
I suppose you can use a lambda (and save it in a std::function) to stock values.
Something as (caution: code not tested) (thanks to Oliv for a correction)
template <typename ... Ts>
class Builder
{
private:
std::function<Result()> fn;
public:
Builder (Ts const & ... ts) : fn{ [=]{ return Result{ts...}; }
{ }
Result build ()
{ return fn(); }
};
edited Nov 14 '18 at 22:25
answered Nov 14 '18 at 14:34
max66max66
35.5k73964
35.5k73964
1
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
@Oliv - sure:Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one constructBuilderwith a value that is destroyed before thebuild()calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.
– max66
Nov 14 '18 at 15:10
1
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.[=]
– Oliv
Nov 14 '18 at 19:31
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
add a comment |
1
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
@Oliv - sure:Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one constructBuilderwith a value that is destroyed before thebuild()calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.
– max66
Nov 14 '18 at 15:10
1
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.[=]
– Oliv
Nov 14 '18 at 19:31
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
1
1
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
Should not the ts... be captured? Then all these values are going to be stored on the heap, that is really expensive for such a small functionality.
– Oliv
Nov 14 '18 at 14:47
@Oliv - sure:
Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one construct Builder with a value that is destroyed before the build() calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.– max66
Nov 14 '18 at 15:10
@Oliv - sure:
Builder (Ts & ... ts) : fn{ { return Result{ts...}; }; but it's dangerous; if one construct Builder with a value that is destroyed before the build() calling, the corresponding saved reference is a dangling reference. Maybe it's possible to do something with perfect forwarding, but you have to templatize the constructor.– max66
Nov 14 '18 at 15:10
1
1
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.
[=]– Oliv
Nov 14 '18 at 19:31
Your code is ill formed you must capture! And in order not to have UB parameters have to be captured by value.
[=]– Oliv
Nov 14 '18 at 19:31
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
@Oliv - so, it seems to me, is better to receive arguments as Ts const & ...; maybe, this way, some copy are avoided (but I suspect that the compilers optimize this aspect).
– max66
Nov 14 '18 at 20:09
add a comment |
One of the solutions I found is to pass intermediary tuple<...> that contains the fields and then unpack it using the mechanism described in "unpacking" a tuple to call a matching function pointer :
// unpacking helpers
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;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
Then it behaves properly:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
Still, maybe it's possible to do something simpler without that tuple intermediary?
add a comment |
One of the solutions I found is to pass intermediary tuple<...> that contains the fields and then unpack it using the mechanism described in "unpacking" a tuple to call a matching function pointer :
// unpacking helpers
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;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
Then it behaves properly:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
Still, maybe it's possible to do something simpler without that tuple intermediary?
add a comment |
One of the solutions I found is to pass intermediary tuple<...> that contains the fields and then unpack it using the mechanism described in "unpacking" a tuple to call a matching function pointer :
// unpacking helpers
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;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
Then it behaves properly:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
Still, maybe it's possible to do something simpler without that tuple intermediary?
One of the solutions I found is to pass intermediary tuple<...> that contains the fields and then unpack it using the mechanism described in "unpacking" a tuple to call a matching function pointer :
// unpacking helpers
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;
};
// Builder with 0 fields returns an empty tuple
template <class... Ts> class Builder {
public:
tuple<> compute_tuple() {
return {};
}
};
template <class T, class... Ts>
class Builder<T, Ts...> : public Builder<Ts...> {
public:
Builder(T t, Ts... ts) : Builder<Ts...>(ts...), tail(t) {}
Result build() {
// get argument tuple
auto arguments = compute_tuple();
// use argument tuple as Result's argument
return build_recursively(typename gens<1 + sizeof... (Ts)>::type{}, arguments);
}
protected:
// computing tuple - just join current element with superclass' result
tuple<T, Ts...> compute_tuple() {
const tuple<T> head{field};
const tuple<Ts...> tail = Builder<Ts...>::compute_tuple();
return tuple_cat(head, tail);
}
private:
template<int ...S>
Result build_recursively(seq<S...>, tuple<T, Ts...> data) {
// invoked matching Result's constructor
return { std::get<S>(data) ... };
}
const T field;
};
Then it behaves properly:
Builder<string, string> b1{"a", "b"};
b1.build(); // invokes Result(string, string)
Still, maybe it's possible to do something simpler without that tuple intermediary?
edited Nov 15 '18 at 2:30
answered Nov 14 '18 at 14:25
Adam KotwasinskiAdam Kotwasinski
2,538827
2,538827
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53302451%2fusing-fields-stored-in-variadic-data-structure-as-method-arguments%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
And why not just hold a
std::tupleand use the many standard library utilities to make ones life easier?– StoryTeller
Nov 14 '18 at 14:28
The builder is unfortunately only a simplification, in real scenario it's a composite buffer composed of multiple sub-buffers that return the value (the field
taildoes not exist).– Adam Kotwasinski
Nov 14 '18 at 14:34
2
The solution utilizes the
tailfield. So if the example is not indicative, and the solution is not applicable, what was accomplished here?– StoryTeller
Nov 14 '18 at 14:35