I am trying to create a factory method that instantiates a template class by passing the constructor arguments for the component classes as template parameter packs.
The ProducerConsumer
template is parameterized on the types of the Producer
and Consumer
, which are aggregated by ProducerConsumer
. Arguments to the factory function depend on the constructor arguments of the parameterizing classes.
I've studied How can I have multiple parameter packs in a variadic template?; the highest voted answer only counts the arguments in the parameter pack, it does not use them. I've also looked Multiple parameter packs — how?, which is rather uninformative for my problem as it passes a variable number of arguments of the same type.
What I have now generates errors at the std::make_tuple
about not being able to cast to tuple
's private base class.
How do I forward a variable number and types of arguments to two different constructors within the factory function, ProducerConsumer::create()
? Also, if and how should r-value reference be applied to the parameter packs being forwarded?
#include <iostream>
#include <tuple>
#include <chrono>
using Duration = std::chrono::steady_clock::duration;
struct ProducerBase {
ProducerBase(Duration maxWait)
: maxWait_{ maxWait }
{}
Duration maxWait_;
};
struct ConsumerBase {
ConsumerBase(int capacity, Duration maxWait) :
capacity_{ capacity },
maxWait_{ maxWait }
{}
int capacity_;
Duration maxWait_;
};
template <typename Producer, typename Consumer>
struct ProducerConsumer {
template < typename ... ProducerArgs, typename ... ConsumerArgs >
static ProducerConsumer* create(
std::tuple<ProducerArgs&& ...> producerArgs,
std::tuple<ConsumerArgs&& ...> consumerArgs
)
{
auto producer = new Producer(std::forward<ProducerArgs>(producerArgs)...);
auto consumer = new Consumer(std::forward<ConsumerArgs>(consumerArgs)...);
return new ProducerConsumer<Producer, Consumer>(producer, consumer);
}
ProducerConsumer(ProducerBase* producer, ConsumerBase* consumer)
:
producer_{ producer },
consumer_{ consumer }
{}
ProducerBase* producer_;
ConsumerBase* consumer_;
};
using namespace std::chrono_literals;
int main(int argc, char* argv[]) {
Duration const MaxWait{ 10s };
int const Capacity{ 32 };
using PC = ProducerConsumer<ProducerBase, ConsumerBase>;
auto pc = PC::create(std::make_tuple(MaxWait), std::make_tuple(Capacity, MaxWait));
return 0;
}
Two issues:
forwarding reference is reserved to T&&
, C<T&&>
is not forwarding reference.
tuple variable is not a variadic variable, you cannot use ...
on it. in C++17, there is std::make_from_tuple
to do what you want:
template < typename ... ProducerArgs, typename ... ConsumerArgs >
static ProducerConsumer* create(
std::tuple<ProducerArgs...> producerArgs,
std::tuple<ConsumerArgs...> consumerArgs
)
{
auto producer = new Producer(std::make_from_tuple<Producer>(producerArgs));
auto consumer = new Consumer(std::make_from_tuple<Consumer>(consumerArgs));
return new ProducerConsumer<Producer, Consumer>(producer, consumer);
}
And as you don't use variadic, you might simplify it to
template < typename ProducerTupleArgs, typename ConsumerTupleArgs >
static ProducerConsumer* create(
ProducerTupleArgs&& producerArgs,
ConsumerTupleArgs&& consumerArgs
)
{
auto producer = new Producer(std::make_from_tuple<Producer>(std::forward<ProducerTupleArgs>(producerArgs)));
auto consumer = new Consumer(std::make_from_tuple<Consumer>(std::forward<ConsumerTupleArgs>(consumerArgs)));
return new ProducerConsumer<Producer, Consumer>(producer, consumer);
}