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);
...
}
Related
Given this code
struct data {
int velocity;
};
template <typename Data>
class Collector {
// ...
public:
void add(const Data& data) {}
template <typename T>
T average1(T Data::*field) const {
return T{}; // Some calculation here
}
template <T Data::*field>
T average2() const {
return T{}; // Some calculation here
}
};
void foo() {
Collector<data> collector;
// I have no problem handling the average by sending member as parameter
auto ok = collector.average1(&data::velocity);
// But compilation here fails
auto error = collector.average2<&data::velocity>();
}
My intention is to replace passing pointers to members to functions by template arguments, but could not match simultaneously member type and member, I can do something like
template <typename T, T Data::*field>
T average2() const {
return T{}; // Some calculation here
}
but then I have to invoke as
auto error = collector.average2<int, &data::velocity>();
and that is a but ugly and seems unnecessary
Do you have an idea on how to fix this or a better approach to collect this kind of data?
Thanks in advance
In C++17, you can make the templated version work by broadening the value template parameter to auto and resolve T later down the line, with a decltype() for example.
#include <type_traits>
template <typename Data>
class Collector {
// ...
public:
template <auto field>
auto average2() const {
using T = decltype(std::declval<Data>().*field);
return T{}; // Some calculation here
}
};
void foo() {
Collector<data> collector;
// Works perfectly fine
auto error = collector.average2<&data::velocity>();
}
In C++20, you can make this cleaner still by constraining field to Data member pointers. This will give you tighter overload resolution as well as nicer error messages.
#include <type_traits>
template<typename PtrT, typename ObjT>
concept MemberObjectPointerFor = std::is_member_object_pointer_v<PtrT> &&
requires(PtrT ptr, ObjT& obj) {
{ obj.*ptr };
};
template <typename Data>
class Collector {
// ...
public:
template <MemberObjectPointerFor<Data> auto field>
auto average2() const {
using T = decltype(std::declval<Data>().*field);
return T{}; // Some calculation here
}
};
The goal
I try to create a set of classes that removes boilerplate code for implementing extensions to a game in C++.
For that, I have a designated value class, that can hold one of the following types:
float, std::string, bool, std::vector<value>, void
For that, I would like to have a host class to which I can add one or more method instances like follows:
using namespace std::string_literals;
host h;
h.add(
method<bool, req<std::string>, req<std::string>, opt<bool>>("compare_strings"s,
[](std::string s_orig, std::string s_comp, std::optional<bool> ingore_case) -> bool {
if (ignore_case.has_value() && ignore_case.value()) {
// ... lowercase both
}
return s_orig.compare(s_comp) == 0;
}));
Note that req<T> should be a meta info that a given value is required, opt<T> a meta info that a given value is not required and may only be provided after all required parameters.
The host class now contains a method execute(std::string function, std::vector<value> values) with function and values originating from a method getting char* for method and ´char** argv+ int argcfor values. Theexecutemethod now is supposed to call the correctmethod` instances function
value host::execute(std::string function, std::vector<value> values) {
// get matching method group
std::vector<method> mthds = m_methods[function];
// get matching parameter list
for (method& mthd : mthds) {
if (mthd.can_call(mthds, values)) {
// call generic method
auto res = mthd.call_generic(values);
// pass result back to callee
// return [...]
}
}
// return error back to callee
// return [...]
}
which means that the actual method class now needs to mangle two methods properly can_call and call_generic.
The value class has corresponding template<typename T> bool is() and template<typename T> T get() methods.
What remains
I did have other attempts at this, but as those failed, I deleted them (not very smart in hindside, but needed to get the whole thing out as another person relied on the results working) and now cannot figure out another attempt then prior ... so this is what I am left with as of now:
class method_base
{
public:
template<typename T> struct in { using type = T; };
template<typename T> struct opt { using type = T; };
public:
virtual bool can_call(std::vector<sqf::value> values) = 0;
virtual sqf::value call_generic(std::vector<sqf::value> values) = 0;
};
template<typename T, typename ... TArgs>
class method : public method_base
{
func m_func;
sqf::value val
public:
using func = T(*)(TArgs...);
method(func f) : m_func(f) {}
virtual retval can_call(std::vector<sqf::value> values) override
{
}
};
Appendix
If something is unclear, confusing or you just have further questions, please do ask them. I will try my best to rephrase whatever is unclear as this will help greatly with developing further extensions in the future, possibly defining a "go to" way for how to create extensions in the community for the game in question (Arma 3 just in case somebody wondered)
I may note that this is pretty much my first deep dive into meta programming so things I present may not be possible at all. If so, I kindly would like to ask you if you may also explain why that is so and the thing I attempt is not possible.
The Solution
I do want to express my thanks to all who answered this question again. I ended up combining pretty much parts of all solutions here and pretty much learned a lot on the way. The final implementation I ended up with looks like the following:
namespace meta
{
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
template <typename ArgType>
struct def_value { static ArgType value() { return {}; } };
template <typename ArgType>
struct get_type { using type = ArgType; };
template <typename ArgType>
struct get_type<std::optional<ArgType>> { using type = ArgType; };
}
struct method {
std::function<bool(const std::vector<value>&)> m_can_call;
std::function<value(const std::vector<value>&)> m_call;
template <typename ... Args, std::size_t... IndexSequence>
static bool can_call_impl(const std::vector<value>& values, std::index_sequence<IndexSequence...> s) {
// values max args
return values.size() <= sizeof...(Args) &&
// for every Arg, either...
(... && (
// the value provides that argument and its the correct type, or...
(IndexSequence < values.size() && sqf::is<sqf::meta::get_type<Args>::type>(values[IndexSequence])) ||
// the value does not provide that argument and the arg is an optional
(IndexSequence >= values.size() && sqf::meta::is_optional_v<Args>)
));
}
template <typename Ret, typename ... Args, std::size_t... IndexSequence>
static value call_impl(std::function<Ret(Args...)> f, const std::vector<value>& values, std::index_sequence<IndexSequence...>) {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(f,
(IndexSequence < values.size() ? sqf::get<sqf::meta::get_type<Args>::type>(values[IndexSequence])
: sqf::meta::def_value<Args>::value())...)
};
}
public:
template <typename Ret, typename ... Args>
method(std::function<Ret(Args...)> f) :
m_can_call([](const std::vector<value>& values) -> bool
{
return can_call_impl<Args...>(values, std::index_sequence_for<Args...>{});
}),
m_call([f](const std::vector<value>& values) -> value
{
return call_impl<Ret, Args...>(f, values, std::index_sequence_for<Args...>{});
})
{
}
bool can_call(const std::vector<value>& values) const { return m_can_call(values); }
value call_generic(const std::vector<value>& values) const { return m_call(values); }
// to handle lambda
template <typename F>
method static create(F f) { return method{ std::function{f} }; }
};
Assumming a way to check current type of value (template <typename T> bool value::isA<T>()) and a way to retrieve the value (template <typename T> /*const*/T& get(/*const*/ value&))
It seems you might do:
struct method
{
template <typename Ret, typename ... Ts>
method(std::function<Ret(Ts...)> f) : method(std::index_sequence<sizeof...(Ts)>(), f)
{}
template <typename Ret, typename ... Ts, std::size_t ... Is>
method(std::index_sequence<Is...>, std::function<Ret(Ts...)> f) :
isOk([](const std::vector<value>& values) {
return ((values.size() == sizeof...(Is)) && ... && values[Is].isA<Ts>());
}),
call([f](const std::vector<value>& values){
return f(get<Ts>(values[Is])...);
})
{}
// to handle lambda
template <typename F>
static fromCallable(F f) { return method{std::function{f}}; }
std::function<bool(const std::vector<value>&)> isOk;
std::function<value(const std::vector<value>&)> call;
};
Here's a quick example including the machinery for ret<T> and opt<T>. You haven't given any information on what value is, so I'm going to assume something like:
struct value {
// using `std::monostate` instead of `void`
std::variant<float, std::string, bool, std::vector<value>, std::monostate> data;
};
(I'm assuming c++17 for this answer.)
From there, we need our metatypes and a few traits to branch off them. I implement them using partial specialisations, but there are other ways too.
// types to determine optional vs. required
template <typename T>
struct req { using type = T; };
template <typename T>
struct opt { using type = T; };
// trait to determine if it's an optional type
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<opt<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
// get the "real" function parameter type
template <typename ArgType>
struct real_type;
template <typename ArgType>
using real_type_t = typename real_type<ArgType>::type;
template <typename T>
struct real_type<req<T>> { using type = T; };
template <typename T>
struct real_type<opt<T>> { using type = std::optional<T>; };
Now we implement method. I'll use a similar polymorphic relationship with method_base as you do in your partial demo; I also template on the function type passed in, to allow e.g. the functions to use const references to the type instead of the type itself.
The implementation itself uses the common trick of delegating to helper functions with std::index_sequence and fold expressions to "iterate" through the variadic template args.
// base class for polymorphism
struct method_base {
virtual ~method_base() = default;
virtual bool can_call(const std::vector<value>& values) const = 0;
virtual value call_generic(const std::vector<value>& values) const = 0;
};
// provide a different method implementation for each set of args
// I also overload on
template<typename RetType, typename Fn, typename... Args>
struct method : method_base {
private:
Fn func;
static_assert(std::is_invocable_r_v<RetType, Fn, real_type_t<Args>...>,
"function must be callable with given args");
public:
// accept any function that looks sort of like what we expect;
// static assert above makes sure it's sensible
template <typename G>
method(G&& func) : func(std::forward<G>(func)) {}
template <std::size_t... Is>
bool can_call_impl(const std::vector<value>& values, std::index_sequence<Is...>) const {
// for every Arg, either...
return (... and (
// the value provides that argument and its the correct type, or...
(Is < values.size() and std::holds_alternative<typename Args::type>(values[Is].data))
// the value does not provide that argument and the arg is an optional
or (Is >= values.size() and is_optional_v<Args>)
));
}
bool can_call(const std::vector<value>& values) const override {
return can_call_impl(values, std::index_sequence_for<Args...>{});
}
template <std::size_t... Is>
value call_generic_impl(const std::vector<value>& values, std::index_sequence<Is...>) const {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(func,
(Is < values.size() ? std::get<typename Args::type>(values[Is].data)
: real_type_t<Args>{})...)
};
}
value call_generic(const std::vector<value>& values) const override {
return call_generic_impl(values, std::index_sequence_for<Args...>{});
}
};
I'll also create a helper function to make methods:
template <typename RetType, typename... Args, typename Fn>
std::unique_ptr<method_base> make_method(Fn&& func) {
return std::make_unique<method<RetType, std::decay_t<Fn>, Args...>>(std::forward<Fn>(func));
}
Live example.
It's not perfect, but this should give you a general idea of how to do it.
Change your method to:
method< R(Args...) >
your tags seem useless. Detect optional with ... std::optional.
For storage, use std variant. Use some non-void type for void (I don't care what).
As a first pass we aim for perfect compatibility.
template<class...Args>
struct check_signature {
bool operator()( std::span<value const> values ) const {
if (sizeof...(Args) != values.size()) return false;
std::size_t i=0;
return (std::holds_alternative<Args>(values[i++])&&...);
}
};
this can be stored in a std::function<bool(std::span<value const>)> or just called in your class impementation.
Similar code can store the callable.
template<class F, class R, class...Args>
struct execute {
F f;
template<std::size_t...Is>
R operator()( std::index_sequence<Is...>, std::span<value const> values ) const {
if (sizeof...(Args) != values.size()) return false;
return f( std::get<Args>(values[Is])... );
}
R operator()( std::span<value const> values ) const {
return (*this)( std::make_index_sequence<sizeof...(Args)>{}, values );
}
};
some work may have to be done for the fake void.
Your method is now a aggregate.
struct method {
std::function<bool(std::span<value const>)> can_call;
std::function<value(std::span<value const>)> execute;
};
if you want it to be. The two template objects above can be stored in these two std functions.
There are probably tpyos, I just wrote this on my phone and have not tested it.
Extending this to cover optional args is a little bit of work. But nothing hard.
In both cases, you'll write a helper function that says if the argument is compatible or generates the value based on if you are past the end of the incoming vector.
Ie, std::get<Args>(values[Is])... becomes getArgFrom<Is, Args>{}(values)..., and we specialize for std optional producing nullopt if Is>values.size().
I'm trying to modernise some GStreamer code by adding smart pointers. So for instance:
GstElement *pipeline = gst_pipeline_new("test-pipeline");
gst_object_unref(pipeline);
can be rewritten:
struct GstElementDeleter {
void operator()(GstElement* p) { gst_object_unref(p); }
};
std::unique_ptr<GstElement, GstElementDeleter> pipeline = gst_pipeline_new("test-pipeline");
But gst_object_unref() can be used on any gpointer so it can be rewritten:
template<typename T>
struct GPointerDeleter {
void operator()(T* p) { gst_object_unref(p); }
};
std::unique_ptr<GstElement, GPointerDeleter<GstElement>> pipeline = gst_pipeline_new("test-pipeline");
But what I'd like to do is limit this to only handling types that can be deallocated using gst_object_unref. Is there a way of declaring a template to only work with a list of types - GstElement, GstBus, etc?
Maybe you could make template the operator() (so there is no need to explicit the template parameter defining the smart pointer) and use SFINAE to enable the operator() only for the allowed types
struct GPointerDeleter
{
template <typename T>
typename std::enable_if<std::is_same<T, GstElement>::value
|| std::is_same<T, GstBus>::value
/* or other cases */
>::type operator() (T * p) const
{ gst_object_unref(p); }
};
Or, maybe better, you can add (as suggested by Jarod42 (thanks)) a static_assert() check inside the operator()
struct GPointerDeleter
{
template <typename T>
void operator() (T * p) const
{
static_assert( std::is_same<T, GstElement>::value
|| std::is_same<T, GstBus>::value
/* or other cases */, "some error message" );
gst_object_unref(p);
}
};
Perhaps a type trait? See <type_traits> if you haven't seen these before.
template<typename T>
struct can_gst_unref : std::false_type { };
// for each type...
template<> struct can_gst_unref<GstElement> : std::true_type { };
// convenient alias, as is convention for type traits
template<typename T>
inline constexpr bool can_gst_unref_v = can_gst_unref<T>::value;
// now conditionally define operator() in your deleter
struct GstDeleter {
template<typename T>
std::enable_if_t<can_gst_unref_v<T>> operator()(T* p) { gst_object_unref(p); }
};
// Making the function a template instead of the class reduces clutter at usage
std::unique_ptr<GstElement, GstDeleter> works(gst_pipeline_new("test-pipeline"));
// can_gst_unref is not specialized to std::string
// so the general case takes over, and gives can_gst_unref_v<std::string> = false
// std::enable_if_t thus doesn't produce a type, and operator() is not defined, because it has no return type
// therefore, this doesn't compile
std::unique_ptr<std::string, GstDeleter> whoops;
I have an application that consists of multiple tasks that share common data using shared memory. Up to now the data in shared memory look like that:
struct Store = {
int id;
Array<Module, 5> modules;
};
where Module is defined as
struct Module = {
uint32_t a;
char b[64];
Array<Component, 10> components;
};
This Store structure has a fixed size an can be easily used within shared memory.
But now I have to support other Modules, lets say ModuleA and ModuleB. Within the normal C++ context I would model these as:
struct ModuleBase {
// common informations
};
struct ModuleA : public ModuleBase {
// ...
};
struct ModuleB : public ModuleBase {
// ...
};
and replace Module by Module* in the Store.
But within the shared memory this is not so easy.
Accessing data in shared memory is easy for fix structures that's why a compile time array is used. I would like to have this property with my different module's.
Idea 1
union Module {
ModuleType type;
ModuleA moduleA;
ModuleB moduleB;
};
The problem is that my Module classes have constructors and that is not allowed inside the union. Access is easy using the type and then Module.moduleX
fix: remove need of constructors
Idea 2
Using a template that evaluates the maximum size of given classes, e.g.
const size_t max_module_size = MaxTMP<ModuleA, ModuleB>::value;
This is the size of the buffer I need to store the modules:
char ModuleBuffer[max_module_size];
(maybe the ModuleBuffer has to be encapsulated in a struct, for usage with Array)
Access is tricky, the content of ModuleBuffer has to be casted to ModuleBase and according to the type to ModuleX. That for I think I need some reinterpret_cast. And I also need to cast the 'ModuleX' in some way to put into the ModuleBuffer.
Question
I don't like both ideas but I cannot imagine another way to handle this problem. Do you have any comments or - even better - solutions?
Effectively, you are between a rock and a hard place.
I would give a try to boost::variant, because of the facilities it comes with, otherwise it's not too difficult to recreate a similar thing, but it is long...
On top of size, you also need to take care about alignment. It will help to use C++11 here, although it is possible to write this in C++03 with a couple libraries/extensions.
Note that a union is not anything so special, and you can easily implement your own, in a way, and like boost::variant make it "tagged".
A couple helpers will help nicely:
/// Size and Alignment utilties
constexpr size_t max(size_t t) { return t; }
template <typename... U>
constexpr size_t max(size_t l, size_t r, U... tail) {
return l > max(r, tail...) ? l : max(r, tail...);
}
template <typename... T>
struct size { static size_t const value = max(sizeof(T)...); };
template <typename... T>
struct alignment { static size_t const value = max(alignof(T)...); };
/// Position of a type in the list
template <typename...> struct position;
template <typename T>
struct position<T> {
static size_t const value = 0;
};
template <typename T, typename Head, typename... Tail>
struct position<T, Head, Tail...> {
static size_t const value =
std::is_same<T, Head>::value ? 0 : 1 + position<T, Tail...>::value;
};
/// Type at a given position
template <size_t, typename...> struct at;
template <size_t N, typename T, typename... Tail>
struct at<N, T, Tail...> { typedef typename at<N-1, Tail..>::type type; };
template <typename T, typename... Tail>
struct at<0, T, Tail...> { typedef T type; };
Now the true fun starts: how to apply a function in a typesafe manner with a type that may change at runtime :x ?
/// Function application
template <typename...> struct Apply;
template <typename H, typename... Tail>
struct Apply<H, Tail...> {
// Mutable
template <typename Func>
static void Do(Func& f, void* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
template <typename Func>
static void Do(Func const& f, void* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
// Const
template <typename Func>
static void Do(Func& f, void const* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H const*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
template <typename Func>
static void Do(Func const& f, void const* storage, size_t tag) {
if (tag == 0) { f(*reinterpret_cast<H const*>(storage)); }
else { Apply<Tail...>::Do(f, storage, tag-1); }
}
}; // struct Apply
/// We need recursion to end quietly even though `tag` is a runtime argument
/// we place the precondition that `tag` should be a valid index in the type
/// list so this should never be reached.
template <>
struct Apply<> {
template <typename... T>
static void Do(T...&&) { abort(); }
}; // struct Apply
Now we can use this to dynamically dispatch in a type safe manner.
/// Variant itself
template <typename... List>
class Variant {
public:
/// Constructor & co
Variant() {
typedef typename at<0, List...>::type First;
new (&_storage) First();
}
Variant(Variant const& other) {
this->initialize(other);
}
Variant& operator=(Variant const& other) {
this->destroy();
this->initialize(other);
return *this;
}
~Variant() { this->destroy(); }
/// Conversions
template <typename T>
explicit Variant(T const& t) {
_tag = position<T, List...>::value;
new (&_storage) T(t);
}
template <typename T>
Variant& operator=(T const& t) {
_tag = position<T, List...>::value;
this->destroy();
new (&_storage) T(t);
return *this;
}
/// Applying a func
template <typename Func>
void apply(Func& f) { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func& f) const { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func const& f) { Apply<List...>::Do(f, &_storage, _tag); }
template <typename Func>
void apply(Func const& f) const { Apply<List...>::Do(f, &_storage, _tag); }
private:
void initialize(Variant const& v) {
struct {
template <typename T>
void operator()(T& t) const { new (_storage) T(t); }
void* _storage;
} copier = { &_storage };
v.apply(copier);
_tag = v._tag;
}
void destroy() {
struct {
template <typename T>
void operator()(T& t) const { t.~T(); }
} eraser;
this->apply(eraser);
}
std::aligned_storage<size<List...>::value,
alignment<List...>::value> _storage;
size_t _tag;
}; // class Variant
Did I say easy ?
Well, there is a subtle issue still: the operator= implementations are not exception safe. In your case it should not be an issue, since you do not have dynamic memory allocation in those types.
References:
std::aligned_storage
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;
}