Passing in member variables of a specified class - c++

I want to pass in the name of a member variable. I thought I could do this by
template <typename T::*>
void SetVal(T::* newval)
{
};
This obviously doesn't work, but hopefully gets across what I'm trying to do. I want to be able to set a certain member variable of the templated class.

You can always put compilation-defined constant as template arguments. So here that would be:
template <typename T, typename R, R T::* member>
R& SetVal(T& t, const R& value)
{
t.*member = value;
return t.*member;
}
struct A
{
int a;
};
int main()
{
A a;
SetVal<A,int,&A::a>(a, 10);
return 0;
}

Related

How to define a class template with reference type template parameter of template template parameter type

I would like to define a class template (hereafter called C) which takes a reference to an object of an to-be instantiated class template (hereafter called S) as template parameter. The objective is that C can be fully instantiated with one template argument.
S is a class template on its own which has one integral type template parameter. The C class template shall be instantiated using a reference to an object of any instantiation of S.
This is what I am trying to achieve:
template<int I> struct S {
int get() { return 42 + I; }
};
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ my desperate attempt
template< typename S<int I>::template & n>
struct C {
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
The compiler I am using supports GNU++11 or older.
In C++17, you might do
template<int I> struct S { int get() { return 42 + I; } };
template <auto& n>
struct C;
template <int I, S<I>& n>
struct C<n>
{
int get() { return n.get(); }
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
Demo.
Before C++17, template <auto& n> struct C; has to be replaced. for example by
template <typename T, T& n> struct C;
template <typename T, T& n>
struct C;
template <int I, S<I>& n>
struct C<S<I>, n>
{
int get() { return n.get(); }
};
S<42> s;
#define AUTO(x) decltype(x), x
int main()
{
C<S<42>, s> c;
// C<AUTO(s)> c;
return c.get();
}
Demo
There is no way that I know in C++11 that will allow you to change just the template parameters to do what you want. What you can do though is not have a non-type template parameter, but just a type, and add a constructor to C that takes the reference to the desired object as a parameter:
template<typename T>
struct C {
C(T &t): t(t) {}
int get() {
return t.get();
}
private:
T &t;
};
Then you could declare c as follows:
C<decltype(s)> c(s);
However, it is of course not so nice to have to repeat yourself like that, so the trick is to make a templated function that will construct a C of the right type for you:
template<typename T>
C<T> make_C(T &t) {
return C<T>(t);
}
And then you can write:
auto c = make_C(s);
This is not the answer. But maybe this helps somebody who stumbled upon this question or even help somebody to actually find the answer.
In contrast to the original question I added the static member variable S.i.
template<int I> struct S {
static constexpr int i = I;
int get() { return 42 + I; }
};
template<int I, S<I>& n> struct C
{
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s.i, s> c;
return c.get();
}
This is not the answer, because still two template arguments are required in order to instantiate the class template C.
This compiles with C++11.

initialize std::array with 'this' pointer

I am trying to initialize an array inside a template class and pass the this pointer to all elements in the array.
This is what my classes might look like:
template<int NUM> class outer_class;
template<int N>
class inner_class {
private:
outer_class<N> *cl;
public:
inner_class(outer_class<N> *num) {
cl = num;
}
void print_num() {
cl->print_num();
}
};
template<int NUM> class outer_class {
private:
int number = NUM;
// --> here I basically want NUM times 'this' <--
std::array<inner_class<NUM>, NUM> cl = { this, this, this, this };
public:
void print_num() {
std::cout << number << std::endl;
}
void print() {
cl[NUM - 1].print_num();
}
};
int main() {
outer_class<4> t;
t.print();
return 0;
}
How can I pass the this pointer to all elements of inner_class stored in the array of outer_class (in C++11)?
First, you can't have this like this outside of constructor or any other member function. Here you have to initialize cl within initializer list.
Using delegating constructor together with std::*_sequence stuff:
template<int NUM> class outer_class {
...
template <std::size_t... Integers>
outer_class(std::index_sequence<Integers...>)
: cl{(static_cast<void>(Integers), this)...}
{}
public:
outer_class(/* whatever */) : outer_class(std::make_index_sequence<NUM>{}) {}
};
Side notes:
Your print member functions should be marked const as they don't modify your members.
cl[NUM - 1].print_num(); you might want to use std::array::back().
You could use some helper functions and then initialize the member using these functions, e.g.:
template <std::size_t I, class T>
T copy(T t) { return t; }
template <class T, std::size_t... Is>
constexpr std::array<T, sizeof...(Is)> copy_n(T const& t, std::index_sequence<Is...>) {
return {copy<Is>(t)... };
}
template <class T, std::size_t N>
constexpr std::array<T, N> copy_n(T const& t) {
return copy_n(t, std::make_index_sequence<N>{});
}
Then in your class:
std::array<inner_class<NUM>, NUM> cl;
outer_class() : cl(copy_n<inner_class<NUM>, NUM>(this)) { }
Note:
[to be verified] you cannot use this in default member initializer, so you need to have a custom constructor;
you need to explicitly specify inner_class<NUM> as the first template parameter for copy_n, because otherwize T will be deduced as outer_class<NUM>*, and while there is an implicit conversion from outer_class<NUM>* to inner_class<NUM>, there is no conversion from std::array<outer_class<NUM*>, NUM> to std::array<inner_class<NUM>, NUM>;
if you are using C++11 and not 14, or clang, you might get a warning on the return of copy_n, you can get rid of it by adding an extra pair of brackets {}.

template class that derives from same template

I have an equivalent to following code:
struct Empty {
static constexpr int id = 0;
};
template <typename Self, typename Base = Empty> struct Compound : public Base
{
int get_id() const
{
return Self::id;
}
};
struct A : Compound<A>
{
static constexpr int id = 0xa;
};
struct B : Compound<B, A>
{
static constexpr int id = 0xb;
};
template <typename T, typename Base> int get_id(const Compound<T, Base> &c)
{
return c.get_id();
}
int test_a()
{
A var;
return get_id(var);
}
int test_b()
{
B var;
return get_id(var);
}
test_b doesn't compile with following error:
error: no matching function for call to 'get_id(B&)'
return get_id(var);
^
note: candidate: template<class T, class Base> int get_id(const Compound<T, Base>&)
template <typename T, typename Base> int get_id(const Compound<T, Base> &c)
^
note: template argument deduction/substitution failed:
note: 'const Compound<T, Base>' is an ambiguous base class of 'B'
return get_id(var);
I understand why that is. B is derived and is convertible to both Compound<B, A> and Compound<A, Empty>
I'm wondering if it is possible to change (within context of C++14) Compound template and get_id() function such that it would return 0xa for A and 0xb for B and would work for arbitrarily long chains of inheritance.
I know that this can be easily solved with virtual function that is overridden in A and B, but I would like to avoid that if possible. Everywhere these types are used they are known and fixed at compile time so there shouldn't be a need to incur run-time overhead.
Just keep it simple:
template <class T>
auto get_id(T const& c) -> decltype(c.get_id())
{
return c.get_id();
}
You don't need c to be some Compound, you really just want it to have a get_id() member function.
It's not clear from your post why need to go the route of Compound in get_id. You can simply use:
template <typename T> int get_id(T const& c)
{
return T::id;
}

Variadic Template of Templated Classes

I have a struct, defined as follows:
template<typename T>
struct Variable
{
char *name;
constexpr Variable(char *setName) : name(setName)
{
}
};
I want to create a class whose template parameters are a list of those structs. So far, the closest I can get is this:
template<template<typename TF> Variable First, template<TA...> typename Variable ... Args>
class UniformBuffer
{
};
A UniformBuffer might be declared something like this:
// vec3 and vec4 are types defined elsewhere
UniformBuffer light<Variable<vec3>("location"), Variable<vec4>("rotation"), Variable<float>("intensity")>;
Unfortunately, this doesn't compile, giving me the error "expected 'class' before 'Variable'" (though putting "class" in there simply generates another error, saying that it expected a '>' after 'Variable). Even looking at the various other amusingly-named questions about variadic templates, I don't seem to be able to find the answer for this. What is the correct syntax for what I am trying to do?
It seems you are looking for a specialization:
template<typename First, typename... Args>
class UniformBuffer;
template<typename First, typename... Args>
class UniformBuffer<Variable<First>,Variable<Args>...>
{
};
Live example
You cannot pass objects of Variable as template parameters, because it cannot be deduced at compile time.
Here is an answer which explains that
You are not allowed to pass class instances in as template arguments, since template arguments require compile time resolved things (like constants, function names, types).
It is unfortunate template arguments cannot be string literals.
What you can do is pass those instances into a helper function, from which you can generate a tuple-like object based on the types of those instances.
template <typename T>
struct Variable
{
typedef T Type;
const char *name;
T val;
constexpr Variable (const char *setName) : name(setName) {}
operator T () const { return val; }
operator T & () { return val; }
};
template <typename... V> UniformBuffer<V...> MakeUniformBuffer (V... args) {
return UniformBuffer<V...>(args...);
}
{
Variable<vec3> loc("location");
Variable<vec4> rot("rotation");
Variable<float> amp("intensity");
auto ub = MakeUniformBuffer(loc, rot, amp);
...
}
The MakeUniformBuffer passes the instances into the constructor of UniformBuffer. UniformBuffer has to unpack the variable template arguments.
template <typename... V> class UniformBuffer;
template <typename V>
struct UniformBuffer <V> {
V val;
UniformBuffer(V v) : val(v) {}
...
};
template <typename V, typename... VV>
struct UniformBuffer<V, VV...> {
V val;
UniformBuffer<VV...> ub;
UniformBuffer(V v, VV... rest) : val(v), ub(rest...) {}
...
};
It is possible to implement set and get methods on UniformBuffer to retrieve buffer elements by name. Below is an illustration of how to implement a get method:
template <typename V>
struct UniformBuffer <V> {
...
typename V::Type get (const Variable<typename V::Type> &v) {
if (v.name != val.name) throw v.name;
return val;
}
template <typename R> R get (const Variable<R> &v) {
throw v.name;
return R();
}
};
template <typename V, typename... VV>
struct UniformBuffer<V, VV...> {
...
typename V::Type get (const Variable<typename V::Type> &v) {
if (v.name != val.name) return ub.get(v);
return val;
}
template <typename R> R get (const Variable<R> &v) {
return ub.get(v);
}
};
{
...
auto ub = MakeUniformBuffer(loc, rot, amp);
auto r = ub.get(rot);
...
}

Class template argument dependent on constructor

With a templated number wrapping struct:
template <int I> struct Num { static const int n = I; };
and a few overloaded functions:
template <typename T>
Num<0> id(T x) { return Num<0>(); }
Num<1> id(int x) { return Num<1>(); }
Num<2> id(double x) { return Num<2>(); }
Num<3> id(char x) { return Num<3>(); }
I can initialise the m_i member of a Zod struct using decltype and the type of the return argument of id:
template <typename T>
struct Zod {
Zod(T x) { m_i = identity<decltype(id(x))>::type::n; }
int m_i;
};
However, what I'd really like is for the Zod struct to have a second integer template argument initialised to the value which m_i was set to.
template <typename T, int I = ?>
struct Zod { ... }
This seems possible, as the identity/decltype expression evaluates to a compile time constant; for example, this is fine at global scope:
char c;
static const int g = identity<decltype(id(c))>::type::n;
The problem is that the x argument of the constructor is not available in the scope of Zod's template declaration. Can it be done?
It's perfectly possible- just pass in *((T*)nullptr) to obtain an lvalue of any type T regardless of it's constructability. After all, all you actually do with the constructor argument is pass it to id and then decltype that, which is perfectly doable in the template, since you know that the type of x is T.
template<typename T, int I = identity<decltype(id(*((T*)nullptr)))>::type::n> struct Zod {
...
};