I have some structs and an enum that look like this:
enum NUM
{
A = 0,
B,
C
};
struct X{};
struct Y{};
I'd like to create a variadic template for a function that looks like this:
template<NUM n, typename...Args>
void func(const Args&...a);
I'd like to specialize the template function func in cases like this:
if NUM::A define func as
template<> void func<A, X>(const X& x)
{
var.emplace_back(std::make_shared<SomeClass>(x));
}
if NUM::B define func as
template<> void func<B,X,Y>(const X& x, const Y& y)
{
var.emplace_back(std::make_shared<SomeOtherClass>(x,y))
}
Could you help me tidy it up with variadic templates?
Partial specialisation of functions can be achieved by deferring the implementation of the function to a function object template, which because it is a class template, can be partially specialised.
example:
#include <memory>
#include <vector>
enum NUM
{
A = 0,
B,
C
};
struct X{};
struct Y{};
// guess at missing code in question
struct SomeBase
{
virtual ~SomeBase() noexcept;
};
struct SomeClass : SomeBase
{
SomeClass(const X&);
};
struct SomeOtherClass : SomeBase
{
SomeOtherClass(const X&, const Y&);
};
std::vector<std::shared_ptr<SomeBase>> var;
// helper base class
struct func_impl_common
{
func_impl_common(std::vector<std::shared_ptr<SomeBase>>& var) : var(var) {}
std::vector<std::shared_ptr<SomeBase>>& var;
};
// general template
template<NUM n, typename...Args> struct func_impl;
// now specialise for A X
template<> struct func_impl<A, X> : func_impl_common
{
using func_impl_common::func_impl_common;
void operator()(X const& x) const
{
var.push_back(std::make_shared<SomeClass>(x));
}
};
// now specialise for B X Y
template<> struct func_impl<B, X, Y> : func_impl_common
{
using func_impl_common::func_impl_common;
void operator()(X const& x, Y const& y) const
{
var.push_back(std::make_shared<SomeOtherClass>(x, y));
}
};
// define func in terms of function object template
template<NUM n, typename...Args>
void func(const Args&...a)
{
auto op = func_impl<n, Args...>(var);
op(a...);
}
// test
int main()
{
func<A>(X{});
func<B>(X{}, Y{});
}
However, if this is a real design it seems a little suspect.
Related
The point is to create uniform struct for vertex and for matrix in c++
Pseudocode:
vector<COUNT, T> {
union {
T data[COUNT];
struct { T x, y, z, w; } // if size is 1 -> only x, if 2 -> only x and y ... etc
struct { T u, v; } // same }
}
template<COUNT, VCOUNT, T>
matrix<COUNT, VCOUNT> : vector<COUNT, vector<VCOUNT, T>> {}
And use like this:
void foo() {
v3f pos(0,1,0);
pos.xy /= pos.z;
m4f transform = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,3.456}}
transform.x.y = transform[3][2];
}
I am too close to make this possible, the only thing I can't reach is initialization of matrix:
v4f v; // v4f typedef of vector<4,float>, m4f is matrix<4,4,float>
v = {0,0,1,0}; //work
m4f mat = {v,v,{},v}; //don't work "excess elements in struct initialization"
m4f mat = {{v,v,{}, v}}; // same
Some implementation details
namespace math::detail
{
template<int SIZE, typename T>
struct vector_base {
T _[SIZE];
};
template<typename T>
struct vector_base<1, T> {
union { T _[1]; T x; };
};
template<typename T>
struct vector_base<2, T> {
union { T _[2]; struct { T x, y; }; struct { T u, v; }; };
};
template<typename T>
struct vector_base<3, T> {
union { T _[3]; struct { T x, y, z; }; };
};
template<typename T>
struct vector_base<4, T> {
union { T _[4]; struct { T x, y, z, w; }; };
};
}
namespace math
{
enum VecI { X, Y, Z, W, U = X, V = Y };
template<int SIZE, typename T>
struct vector : detail::vector_base<SIZE, T>
{
using base = detail::vector_base<SIZE, T>;
vector() = default;
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == SIZE, int>::type = 0>
constexpr vector(Types ... s) : base{static_cast<T>(s)... } {}
// some foos, operator overloading
}
template<int size, int vector_size, typename type>
struct matrix : vector<size, vector<vector_size, type>>
{
// special foos only for matrix
}
}
Vector constructor with parameter pack is not compiling in case of matrix:
error: no matching constructor for initialization of 'vector<3, vector<3, float> >'
return {{{1,0,0}, {0,1,0}, {0,0,1}}};
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/math/vector/base.h:62:15: note: candidate template ignored: requirement 'sizeof...(Types) == 3' was not satisfied [with Types = <>]
constexpr vector(Types ... s) : base{static_cast<T>(s)... } {}
^
Next implementation of matrix at least compile, but need initializing with extra braces {{{},{},{}} and can't inherit some vector properties:
template<int size, int vector_size, typename type>
struct matrix
{
vector _[size];
// foos
}
I am also tried writing constructors for vector_base, for matrix, deduction guides for all of them, nothing works.
How to initialize array of arrays in C++ with automatic deduction (can initialize {5, 0.43, .1f} without errors of type incompatibilities), without implicitly writing types like in m4f mat = {v4f(0,0,1),v4f(0,1,0), v4f(1,0,0)}, and without "excess" nested braces {{{}}}?
My compiler is clang:9.0.0 with c++17, also can use c++2a, or c++20 when it comes.
Answer to your actual question
C++ is just lousy at deducing things from braced lists. It won't look at the lists, notice your constructor is initializing a member with them, and try to fit the braces to that member. Basically, multi-level brace initialization only works if it doesn't have to deduce anything (e.g. a non-template constructor, aggregate initialization, or a varible-length initializer_list)
To get this working for you, give each base type a simple constructor:
template<typename T>
struct vector_base<2, T> {
vector_base(T x, T y) : x(x), y(y) { } // <-- add these to each specialization
union { T _[2]; struct { T x, y; }; struct { T u, v; }; };
};
Then, for the generic derived type, promote the constructor:
template<int SIZE, typename T>
struct vector : detail::vector_base<SIZE, T>
{
using detail::vector_base<SIZE, T>::vector_base;
/* overloads and methods */
};
Now all forms of vector have a non-generic constructor and brace-initialization works as expected.
math::vector<3, math::vector<3, float>> foo {{1,0,0},{0,1,0},{0,0,1}};
Now, about the type punning
There's actually a neat trick for adding array-like access to struct members without breaking strict aliasing here: Overload the [] operator to access a static constexpr array of member-to-pointers to your struct members.
Then there is only one level of indirection between array-like access and member access, in a constant-foldable map. This allows the compiler to, in most cases, produce the same assembly as if you used union punning or a reinterpret_cast to access the member data in a for loop.
Applied to your code:
template <size_t SIZE, typename T>
struct vector_base;
template <typename T>
struct vector_base<1, T> {
constexpr vector_base(T x) : x(x) {}
T x;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[1] = {&vector_base::x};
};
template <typename T>
struct vector_base<2, T> {
constexpr vector_base(T x, T y) : x(x), y(y) {}
union {
T x;
T u;
};
union {
T y;
T v;
};
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[2] = {&vector_base::x, &vector_base::y};
};
template <typename T>
struct vector_base<3, T> {
constexpr vector_base(T x, T y, T z) : x(x), y(y), z(z) {}
T x;
T y;
T z;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[3] = {&vector_base::x, &vector_base::y, &vector_base::z};
};
template <typename T>
struct vector_base<4, T> {
constexpr vector_base(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
T x;
T y;
T z;
T w;
constexpr T& operator [](size_t index) { return this->*(vector_base::map[index]); }
constexpr const T& operator [](size_t index) const { return this->*(vector_base::map[index]); }
static constexpr T vector_base::* map[4] = {&vector_base::x, &vector_base::y, &vector_base::z, &vector_base::w};
};
Note: the union between x/y and u/v is actually well-formed, even for accessing inactive members
Demo: https://godbolt.org/z/ibpSqB
I have an existing wrapper class to wrap Type T, which can be default constructed, or can be constructed with Type U. And the wrapper itself is constructed by a reference to struct Context.
struct Foo {}; // Foo can be default constructed.
// Bar needs an argument to be constructed.
struct Bar {
public:
Bar(int i) {...}
Bar() = deleted;
};
Context cx;
Wrapper<Plain> wrapper{cx};
Wrapper<Bar> wrapper2{cx, 10};
Now I have a class, Obj, which also takes Context as constructor argument.
And for the Wrapper to wrap Obj, now I have to provide the same 2 arguments to the wrapper constructor.
Context cx;
Wrapper<Obj> wrapper3{cx, cx}; // ugly, would prefer Wrapper<Obj> wrapper3{cx};
I tried to use template class specialization on Obj, however there's also some data structures in Wrapper I'd like to use in Wrapper.
Or if I don't have to do full specialization on Obj, how can I use some templates to refactor the existing Wrapper?
Thanks
struct Context {};
template <typename T>
class Wrapper {
public:
Wrapper(Context& cx) : ptr() {
foo(cx);
}
template < typename U>
Wrapper(Context& cx, U u) : ptr(u) {
foo(cx);
}
private:
T ptr;
void foo(Context& cx) {}
};
class Obj {
public:
Obj() = delete;
Obj(Context& cx) {}
};
template <>
class Wrapper<Obj> {
public:
explicit Wrapper(Context& cx) : ptr(cx) {
// how to call foo(Context&) in Wrapper<T>?
// foo(cx);
}
private:
Obj ptr;
};
int main() {
Context cx;
Wrapper<Obj> w(cx); // without template class speicialization on Obj, I have to use Wrapper<Obj> w(cx, cx);
return 0;
}
You can specialize only the constructor to avoid having to redefine the whole class. Also, you can use variadic templates to avoid creating many constructors for each argument:
struct Context {};
template <typename T>
class Wrapper {
public:
template<class... U>
Wrapper(Context& cx, U... u) ;
private:
T ptr;
void foo(Context& cx) {}
};
template <typename T>
template<class... U>
Wrapper<T>::Wrapper(Context& cx, U... u) : ptr(u...) {
foo(cx);
}
class Obj {
public:
Obj() = delete;
Obj(Context& cx) {}
};
template <>
template<class... U>
Wrapper<Obj>::Wrapper(Context& cx, U... u) : ptr(cx, u...) {
foo(cx);
}
struct Foo {};
struct Bar {
public:
Bar(int i) {}
Bar() = delete;
};
int main()
{
Context cx;
Wrapper<Obj> w0(cx);
Wrapper<Foo> w1(cx);
Wrapper<Bar> w2(cx,1);
return 0;
}
Let's say I have a base class foo and two derived classes A and B. I then have another class bar, which has a data members x, y, z, which can be either A,or, but the types depends on other data members x_type, y_type, and z_type, and those values are not available at compile time. I though about using template data member and defining the type in constructor, where I get the values for the types but apparently that is not possible at least in C++11. So how to proceed?
class foo{
public:
foo(double);
int x_type;
virtual double do_something(double, int) = 0;
};
class A: public foo {
public:
A(double, double);
double do_something(double, int);
private:
double z1;
double z2;
};
class B: public foo {
public:
B(double);
double do_something(double, int);
private:
double w;
};
class bar {
public:
bar();
double do_something2(int);
private:
int x_type;
int y_type;
int x_type;
x; // these are either A or B...
y;
z;
};
And in constructor I would have something like
if(x_type == 1){
x = A(arg1, arg2);
} else {
x = B(arg3);
}
In my real application there can be much higher number of derived classes and data members with unknown types. I was wondering if it is possible to make bar a template class with multiple template parameters, but I am not sure if that is possible either as the parameter type depends on another parameter?
You need to use polymorphism and take advantage of the common base class Foo:
private:
int x_type;
int y_type;
int x_type;
std::unique_ptr<Foo> x; // these are either A or B...
std::unique_ptr<Foo> y;
std::unique_ptr<Foo> z;
};
Then in your constructor you can create x y z from the correct type:
if(x_type == 1){
x.reset(new A(arg1, arg2));
} else {
x.reset(new B(arg3));
}
It is a good practice to move the code that creates the correct Foo instance in a so called "factory" class or function in order to hide the decision making logic and construction specifics (which might be quite complex at times).
If all type that can be used for x, y and z are all derived from a common base class, the base-pointer solution, with std::unique_ptr (+1 for Lyubomir Stankov), is (IMHO) a good solution.
But you asked "if it is possible to make bar a template class with multiple template parameters".
Yes: it's possible. Not really elegant (ever IMHO) but possible.
I propose the following solution for fun but I think that, in a more general case (note that, in my example, A and B are unrelated classes, not more derived from foo), can be useful (I hope so)
#include <tuple>
#include <string>
#include <utility>
class A
{
private:
double d;
std::string s;
public:
A (double d0, std::string s0) : d { d0 }, s { s0 } { }
};
class B
{
private:
long l;
public:
B (long l0) : l { l0 } { }
};
template <typename Tx, typename Ty, typename Tz>
class bar
{
private:
template <typename ... Ts>
using tpl = std::tuple<Ts...>;
template <std::size_t ... Is>
using is = std::index_sequence<Is...> const;
template <std::size_t N>
using mis = std::make_index_sequence<N>;
Tx x;
Ty y;
Tz z;
template <typename ... Tsx, std::size_t ... Isx,
typename ... Tsy, std::size_t ... Isy,
typename ... Tsz, std::size_t ... Isz>
bar (tpl<Tsx...> const & tx0, is<Isx...> const &,
tpl<Tsy...> const & ty0, is<Isy...> const &,
tpl<Tsz...> const & tz0, is<Isz...> const &)
: x { std::get<Isx>(tx0) ... },
y { std::get<Isy>(ty0) ... },
z { std::get<Isz>(tz0) ... }
{ }
public:
template <typename ... Tsx, typename ... Tsy, typename ... Tsz>
bar (tpl<Tsx...> const & tx0,
tpl<Tsy...> const & ty0,
tpl<Tsz...> const & tz0)
: bar(tx0, mis<sizeof...(Tsx)> {},
ty0, mis<sizeof...(Tsy)> {},
tz0, mis<sizeof...(Tsz)> {})
{ }
};
int main()
{
bar<A, B, A> aba{ std::make_tuple(2.3, "str1"),
std::make_tuple(4),
std::make_tuple(5.4, "str2") };
bar<B, A, B> bab{ std::make_tuple(3),
std::make_tuple(3.2, "str3"),
std::make_tuple(5) };
}
Unfortunately this example use std::make_index_sequence and std::index_sequence that are C++14 features.
If you want implement foo in C++11, you can implement the following structs struct indexSeq and struct indexSeqHelper, to substitute std::index_sequence and std::make_index_sequence
template <std::size_t ...>
struct indexSeq
{ };
template <std::size_t N, std::size_t ... Next>
struct indexSeqHelper
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; };
template <std::size_t ... Next >
struct indexSeqHelper<0U, Next ... >
{ using type = indexSeq<Next ... >; };
and define is and mis as follows
template <std::size_t ... Is>
using is = indexSeq<Is...>;
template <std::size_t N>
using mis = typename indexSeqHelper<N>::type;
The static type of all variables must be known at compile time, so it cannot change based on the value of a run time object. The way to make this work is to make x, y, and z all have the type std::uniqe_ptr<foo> and then dynamically allocate an A or B object at run time:
class bar {
public:
bar(some_type something) {
if (something == some_value) {
b.x = new A(3.14, 12.34);
} else {
b.x = new B(456.78);
}
}
private:
int x_type;
std::unique_ptr<foo> x;
//...
};
int main() {
bar b(whatever());
}
In this case you should also declare foo::~foo() to be virtual so that you ensure the derived objects get destroyed correctly.
It would also be a generally Good Idea™ to eliminate x_type and friends entirely and write code that doesn't care about the actual type of x once it's created.
I was wondering if it is possible to make bar a template class with multiple template parameters, but I am not sure if that is possible either as the parameter type depends on another parameter?
I don't know if this helps, but I'll let it here just in case.
You see, different specializations of a template can inherit from different classes. So that you can have:
// fwd decl
template <int kind> class bar;
template <> class bar<1> : public A {
public:
bar(double x, double y) : A(x,y) { }
};
template <> class bar<2> : public B {
public:
bar(double a) : B(a) { }
};
At later stage, when you come with a class C : public foo, you just assign another kind to a new bar template specializations and there you have it: using bar as a unifying name (warning... but not a unifying type - not other than the common foo ancestor. bar<1> and bar<2> will be two different types)
So, Ok, if you don't want inheritance, you can have it by different has-a in specific bar template specializations.
Like
template <int kind> class bar;
template <> class bar<1> {
A val;
public:
bar(double x, double y) : val(x,y) { }
void doSomething2(...) {
// use the val of type A
}
};
template <> class bar<2> {
B val;
double y_;
public:
bar(double x, double y) : val(x), y_(y) { }
void doSomething2(...) {
// use the val of type B and a separate y_
}
};
I though about using template data member and defining the type in
constructor, where I get the values for the types but apparently that
is not possible at least in C++11
C++11 comes with the standard schema of dealing with templated construction depending on some parameter by using make_* template function creating appropriate type of object. See e.g. make_tuple function:
auto t = std::make_tuple(1, "abc", 1.0);
// decltype(t) == std::tuple<int, char const*, double>
This can be implemented by creating template class and template construction function:
template <class T>
struct foo {
T t;
foo(T t): t(t) { }
};
template <class T>
foo<T> make_foo(T t) { return foo<T>(t); }
I would like a class to have a function with a template argument, and based on that template argument a particular member variable is manipulated.
For example, if function template specialization were allowed, then something like this:
struct A
{
struct M1 {};
struct M2 {};
// Function template specialization not allowed :(
template<typename M>
void addM(M const &m);
template<>
void addM(M1 const &m)
{
m1_vec_.push_back(m);
}
template<>
void addM(M2 const &m)
{
m2_vec_.push_back(m);
}
std::vector<M1> m1_vec_;
std::vector<M2> m2_vec_;
};
Any ideas? I feel like I'm missing something simple but can't quite put my finger on it.
Just overload them :
struct A
{
struct M1 {};
struct M2 {};
void addM(M1 const &m)
{
m1_vec_.push_back(m);
}
void addM(M2 const &m)
{
m2_vec_.push_back(m);
}
std::vector<M1> m1_vec_;
std::vector<M2> m2_vec_;
};
If you do not wish to duplicate addM's code, you can just abstract the vector choice behind another function :
struct A
{
struct M1 {};
struct M2 {};
template <class T>
void addM(T const &m)
{
getVec<T>().push_back(m);
}
std::vector<M1> m1_vec_;
std::vector<M2> m2_vec_;
private:
template<class T>
std::vector<T> &getVec();
};
template <>
std::vector<A::M1> &A::getVec() { return m1_vec_; }
template <>
std::vector<A::M2> &A::getVec() { return m2_vec_; }
template <typename T, typename C>
class CSVWriter{
template <typename PrinterT>
void write(std::ostream& stream, const PrinterT& printer){
}
};
I want to check whether there exists at least two overloads PrinterT::operator()(T*) and PrinterT::operator()(C*)
PrinterT may or may not inherit from std::unary_function
What concept Checking Classes I need to use here ?
(I am not using C++11)
You can use something like that
#include <iostream>
#include <boost/concept/requires.hpp>
#include <boost/concept/usage.hpp>
template <class Type, class Param>
class has_operator_round_brackets_with_parameter
{
public:
BOOST_CONCEPT_USAGE(has_operator_round_brackets_with_parameter)
{
_t(_p);
}
private:
Type _t;
Param _p;
};
struct X {};
struct Y {};
struct Test1
{
void operator() (X*) const { }
};
struct Test2: public Test1
{
void operator() (X*) const { }
void operator() (Y*) const { }
};
template <class T, class C>
struct CSVWriter
{
template <class PrinterT>
BOOST_CONCEPT_REQUIRES(
((has_operator_round_brackets_with_parameter<PrinterT, T*>))
((has_operator_round_brackets_with_parameter<PrinterT, C*>)),
(void)) write(std::ostream& stream, const PrinterT& printer)
{
}
};
int main()
{
CSVWriter<X, Y> w;
// w.write<Test1>(std::cout, Test1()); // FAIL
w.write<Test2>(std::cout, Test2()); // OK
return 0;
}