Store member get/set functions for later use - c++

I want to store a pair of member get/set functions for later use with an object of type T.
I have a partially working setup, see below. Questions remain however:
How do I (smartly) deal with all possible variants? member_get could be [returning value or const value& or even value& | const or non-const] member_set could be [accepting const &, & or &&]. Sure, 'best practices' would rule out some combinations, but I cannot rely on that as the definition of member_get and member_set is out of my hands.
How do I correctly deal with possible member_set move semantics?
Is there a different/better/simpler general way to approach this?
Notes:
I intentionally left open the exact type S of the setter. Not sure if that's a good or bad idea.
Lambdas obviously come to mind, but I can't see how they can help the issue. The caller of Make( get, set ) is not supposed to supply lambdas. That would be just delegating the problem to him!?
any std::function ideas should be ruled out because of the overhead
template <typename T, typename V, typename G, typename S>
class GetSet
{
public:
constexpr GetSet( G member_get, S member_set ) : Get( member_get ), Set( member_set )
{ }
auto GetValue( const T& t ) const
{
return ( t.*Get )( );
}
void SetValue( T& t, V&& value ) const
{
( t.*Set )( std::forward<V>( value ) );
}
private:
G Get;
S Set;
};
template <typename T, typename ValueType, typename S>
constexpr auto Make( ValueType( T::*member_get )( ) const, S member_set )
{
using G = ValueType( T::* )( ) const;
return GetSet<T, ValueType, G, S>( member_get, member_set );
}

Not sure why you need this, but the simplest solution is below.
#include <utility>
template <class F> struct ClassType;
template <typename Ret, typename TCls, typename... Args>
struct ClassType<Ret (TCls::*)(Args...)> {
using type = TCls;
};
template <typename Ret, typename TCls>
struct ClassType<Ret (TCls::*)() const> {
using type = TCls;
};
template<class TFnGet, class TFnSet>
class GetSet
{
public:
using TGet = TFnGet;
using TSet = TFnSet;
public:
inline GetSet(TGet fnGet, TSet fnSet)
: m_fnGet(fnGet), m_fnSet(fnSet)
{
}
template<class T>
inline decltype(auto) GetValue(T&& r) const
{
static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnGet>::type>::value, "wrong type of r?");
return (r.*m_fnGet)();
}
template<class T, class TValue>
inline void SetValue(T&& r, TValue&& value)
{
static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnSet>::type>::value, "wrong type of r?");
(r.*m_fnSet)(std::forward<TValue>(value));
}
private:
TGet m_fnGet;
TSet m_fnSet;
};
template<class TGet, class TSet>
GetSet<TGet, TSet> MakeGetSet(TGet fnGet, TSet fnSet)
{
static_assert(std::is_same<typename ClassType<TGet>::type, typename ClassType<TSet>::type>::value, "members of different classes?");
return GetSet<TGet, TSet>(fnGet, fnSet);
}
verified with:
class A
{
public:
void Set(int i) {}
int Get() const { return 0;}
void SetRef(char& ch) {}
A& GetRef() { return *this;}
void SetRRef(float&& ) {}
int&& GetRRef() { return 1; }
void SetConstRef(const char& ch) {}
int GetNotConst() { return 0;}
};
int main(int argc, char* argv[])
{
A a;
auto gs = MakeGetSet(&A::Get, &A::Set);
auto gsRef = MakeGetSet(&A::GetRef, &A::SetRef);
auto gs2 = MakeGetSet(&A::GetRRef, &A::SetRRef);
auto gsNonConst = MakeGetSet(&A::GetNotConst, &A::SetConstRef);
int x = gs.GetValue(a);
gs.SetValue(a, 2);
const A& ra = a;
x = gs.GetValue(ra);
A& r = gsRef.GetValue(a);
char ch =' ';
gsRef.SetValue(a, ch);
x = gs2.GetValue(a);
gs2.SetValue(a, 1.1f);
x = gsNonConst.GetValue(a);
gsNonConst.SetValue(a, ch);
std::cout << "ok\n";
return 0;
}

Related

Conditionally Remove Variable Based on Template Type

Currently I am using code that looks like the below.
template<class T, class U = char>
struct ScoredObject {
ScoredObject( T *object, double score, const U &data = U() ) noexcept( noexcept( U( data ) ) ) : object( object ), score( score ), data( data ) {}
T *object;
double score;
U data;
// ...
};
In all cases, except 1, the data variable is not used. It's default is char to just waste as little space as possible. Realistically, I'd like it to be void, but of course then it won't compile.
I know I can do some std::enable_ifs on the constructor, but the issue is still with the data variable. Is it possible to use some sort of template "magic" to remove the data variable when U is void?
Basically, I want to do something like this (which I know isn't valid)
template<typename f = U, typename std::enable_if<!std::is_void<f>::value, bool>::type = true>
U data;
You can inherit Scored_object from different bases depending on the type of U. Something like this:
template<class T, class U>
struct Scored_object_base : Scored_object_base<T, void> {
U data;
using Base = Scored_object_base<T, void>;
Scored_object_base(T* object, double score, const U& data)
: Base(object, score), data(data) {}
};
template<class T>
struct Scored_object_base<T, void> {
T* object;
double score;
Scored_object_base(T* object, double score)
: object(object), score(score) {}
};
template<class T, class U = void>
struct Scored_object : Scored_object_base<T, U> {
using Base = Scored_object_base<T, U>;
using Base::Base;
};
template<class T, class U>
bool operator==(const Scored_object<T, U>& o1, const Scored_object<T, U>& o2) {
const auto f = (*o1.object == *o2.object && o1.score == o2.score);
if constexpr (std::is_void_v<U>)
return f;
else
return f && o1.data == o2.data;
}
int main() {
int i;
Scored_object<int, void> o1(&i, 1);
Scored_object<int, char> o2(&i, 1, 'a');
std::cout << sizeof(o1) << std::endl; // Output: 16
std::cout << sizeof(o2) << std::endl; // Output: 24
}

Variadic sequence of pointer to recursive member of struct/class as template parameter

I'm struggling with some template programming and I hope you can give me some help. I coded a C++11 interface that, given some structs like:
struct Inner{
double a;
};
struct Outer{
double x, y, z, r;
Inner in;
};
Implements a getter/setter to the real data that is customized to the specified struct members:
MyData<Outer, double, &Outer::x,
&Outer::y,
&Outer::z,
&Outer::in::a //This one is not working
> state();
Outer foo = state.get();
//...
state.set(foo);
I managed to implement this for simple structs in the following way:
template <typename T, typename U, U T::* ... Ms>
class MyData{
std::vector<U *> var;
public:
explicit MyData();
void set(T const& var_);
T get() const;
};
template <typename T, typename U, U T::* ... Ms>
MyData<T, U, Ms ... >::Struct():var(sizeof...(Ms))
{
}
template <typename T, typename U, U T::* ... Ms>
void MyData<T, U, Ms ...>::set(T const& var_){
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
*var[i++] = var_.*d;
}
}
template <typename T, typename U, U T::* ... Ms>
T MyData<T, U, Ms ...>::get() const{
T var_;
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
var_.*d = *var[i++];
}
return var_;
}
But it fails when I pass a member of a nested struct. Ideally, I'd like to implement a generic pointer to member type that allows me to be compatible with several levels of scope resolutions. I found this approach, but I'm not sure if this should be applied to my problem or if there exists some implementation ready to use. Thanks in advance!
Related posts:
Implicit template parameters
Pointer to inner struct
You might wrap member pointer into struct to allow easier chaining:
template <typename...> struct Accessor;
template <typename T, typename C, T (C::*m)>
struct Accessor<std::integral_constant<T (C::*), m>>
{
const T& get(const C& c) { return c.*m; }
T& get(C& c) { return c.*m; }
};
template <typename T, typename C, T (C::*m), typename ...Ts>
struct Accessor<std::integral_constant<T (C::*), m>, Ts...>
{
auto get(const C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
auto get(C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
};
template <typename T, typename U, typename ...Ts>
class MyData
{
std::vector<U> vars{sizeof...(Ts)};
template <std::size_t ... Is>
T get(std::index_sequence<Is...>) const
{
T res;
((Ts{}.get(res) = vars[Is]), ...); // Fold expression C++17
return res;
}
template <std::size_t ... Is>
void set(std::index_sequence<Is...>, T const& t)
{
((vars[Is] = Ts{}.get(t)), ...); // Fold expression C++17
}
public:
MyData() = default;
T get() const { return get(std::index_sequence_for<Ts...>()); }
void set(const T& t) { return set(std::index_sequence_for<Ts...>(), t); }
};
With usage similar to
template <auto ...ms> // C++17 too
using Member = Accessor<std::integral_constant<decltype(ms), ms>...>;
MyData<Outer, double, Member<&Outer::x>,
Member<&Outer::y>,
Member<&Outer::z>,
Member<&Outer::in, &Inner::a>
> state;
std::index_sequence is C++14 but can be implemented in C++11.
Folding expression from C++17 can be simulated too in C++11.
typename <auto> (C++17) should be replaced by typename <typename T, T value>.
Demo
A generalization of a member pointer is a function that can map T to X& at compile time.
In c++17 it isn't hard to wire things up thanks to auto. In c++11 it gets harder. But the basic idea is that you don't actually pass member pointers, you pass types, and those types know how to take your class and get a reference out of them.
template<class T, class D, class...Fs>
struct MyData {
std::array<D*, sizeof...(Fs)> var = {};
explicit MyData()=default;
void set(T const& var_) {
var = {{ Fs{}(std::addressof(var_))... }};
}
T get() {
T var_;
std::size_t index = 0;
using discard=int[];
(void)discard{ 0, (void(
*Fs{}(std::addressof(var_)) = *var[index++]
),0)... };
return var_;
}
};
it remains to write a utility that makes writing the Fs... easy for the member pointer case
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M > {
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
#define TYPE_N_VAL(...) \
decltype(__VA_ARGS__), __VA_ARGS__
#define MEM_PTR(...) get_ptr_to_member_t< TYPE_N_VAL(__VA_ARGS__) >
now the basic case is
MyData< Outer, double, MEM_PTR(&Outer::x), MEM_PTR(&Outer::y) >
The more complex case can now be handled.
An approach would be to teach get_ptr_to_member to compose. This is annoying work, but nothing fundamental. Arrange is so that decltype(ptr_to_member_t * ptr_to_member_t) returns a type that instances right, applies it, then takes that pointer and runs the left hand side on it.
template<class First, class Second>
struct composed;
template<class D>
struct composes {};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<class First, class Second>
struct composed:composes<composed<First, Second>> {
template<class In>
auto operator()(In&& in) const
RETURNS( Second{}( First{}( std::forward<In>(in) ) ) )
};
template<class First, class Second>
composed<First, Second> operator*( composes<Second> const&, composes<First> const& ) {
return {};
}
then we upgrade:
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M >:
composes<get_ptr_to_member_t< D T::*, M >>
{
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
and now * composes them.
MyData<TestStruct, double, MEM_PTR(&Outer::x),
MEM_PTR(&Outer::y),
MEM_PTR(&Outer::z),
decltype(MEM_PTR(&Inner::a){} * MEM_PTR(&Outer::in){})
> state();
answre probably contains many typos, but design is sound.
In c++17 most of the garbage evaporates, like the macros.
I would use lambda approach to implement similar functionalities in C++17(C++14 is also ok, just change the fold expression):
auto access_by() {
return [] (auto &&t) -> decltype(auto) {
return decltype(t)(t);
};
}
template<class Ptr0, class... Ptrs>
auto access_by(Ptr0 ptr0, Ptrs... ptrs) {
return [=] (auto &&t) -> decltype(auto) {
return access_by(ptrs...)(decltype(t)(t).*ptr0);
};
}
auto data_assigner_from = [] (auto... accessors) {
return [=] (auto... data) {
return [accessors..., data...] (auto &&t) {
((accessors(decltype(t)(t)) = data), ...);
};
};
};
Let's see how to use these lambdas:
struct A {
int x, y;
};
struct B {
A a;
int z;
};
access_by function can be used like:
auto bax_accessor = access_by(&B::a, &A::x);
auto bz_accessor = access_by(&B::z);
Then for B b;, bax_accessor(b) is b.a.x; bz_accessor(b) is b.z. Value category is also preserved, so you can assign: bax_accessor(b) = 4.
data_assigner_from() will construct an assigner to assign a B instance with given accessors:
auto data_assigner = data_assigner_from(
access_by(&B::a, &A::x),
access_by(&B::z)
);
data_assigner(12, 3)(b);
assert(b.z == 3 && b.a.x == 12);

Template non-type pointer to arbitrary class method

Let's say I have:
struct Foo {
void a();
void b(const int& );
int c();
};
I can create a function that takes as an argument an arbitrary pointer-to-Foo method:
template <typename R, typename... Formal, typename... Args>
R call(Foo* f, R (Foo::*method)(Formal...), Args&&... args) {
return (f->*method)(std::forward<Args>(args)...);
}
int gratuitous = call(&some_foo, &Foo::c);
And I can create a function that takes a specific type of pointer-to-Foo method as a template:
template <void (Foo::*method)()>
void only_for_a(Foo *f) {
(f->*method)();
}
only_for_a<&Foo::a>(&some_foo);
But is there a way to create a function that I can template on any pointer to class method? I want to be able to do:
works_for_anything<&Foo::a>(&some_foo);
works_for_anything<&Foo::b>(&some_foo, 42);
int result = works_for_anything<&Foo::c>(&some_foo);
Would this work for you?
template< typename T, T >
class works_for_anything_t;
template< typename R, typename... Args, R (*f)(Args...) >
class works_for_anything_t< R (*)(Args...), f >{
public:
R operator()( Args... args ){ return f(args...); }
};
template< typename T, typename R, typename... Args, R (T::*f)(Args...) >
class works_for_anything_t< R (T::*)(Args...), f >{
public:
R operator()( T& v, Args... args ) { return (v.*f)(args...); }
works_for_anything_t(T& v)
: v_(v) { }
private:
T& v_;
};
template< typename T, typename R, typename... Args, R (T::*f)(Args...) const >
class works_for_anything_t< R (T::*)(Args...) const, f >{
public:
R operator()( const T& v, Args... args ) const { return (v.*f)(args...); }
works_for_anything_t(const T& v)
: v_(v) { }
private:
const T& v_;
};
#define works_for_anything(f) works_for_anything_t<decltype(&f), &f>
struct Foo {
void a();
void b(const int& );
int c();
};
int test();
int main() {
Foo foo;
works_for_anything(Foo::b){foo}( 42 );
works_for_anything(test){}();
return 0;
}

How can I use and specialize a 'curious repeating template pattern'

I would like to pass in some parameters into a curious repeating template pattern. I would then like this base class to create other objects and pass both types into the subclass. This would allow me to generalize the base class to perform some common logic before the subclass is called. Each subclass should be able to be a specialized instance of the one level hierarchy.
Here is how to do this:
struct ParamOne {
double val {0.0};
};
struct ParamTwo {
int val {0};
};
template<typename P, typename Data, typename Other>
class Baseclass
{
public:
using subclass_type = P;
using data_type = Data;
using other_type = Other;
bool Method( const Data &data);
};
template<typename P, typename Data, typename Other> using pdata_type = typename P::data_type;
template<typename P, typename Data, typename Other> using pother_type = typename P::other_type;
template<typename P, typename Data, typename Other>
bool Baseclass<P, Data, Other>::Method( const Data &data )
{
P& Subclass = static_cast<P&>( *this );
pother_type<P, Data, Other> other;
other.val = 11;
return Subclass.SubclassMethod( data, other );
}
template<typename Data, typename Other>
class Subclass : public Baseclass<Subclass<Data, Other>, Data, Other>
{
public:
using data_type = Data;
using other_type = Other;
bool SubclassMethod( const Data &data, Other &other );
};
template<typename Data, typename Other>
bool Subclass<Data, Other>::SubclassMethod( const Data &data, Other &other )
{
return true;
}
template<>
bool Subclass<ParamOne, ParamTwo>::SubclassMethod( const ParamOne &data, ParamTwo &other )
{
return true;
}
int main(int argc, char **argv)
{
ParamOne one;
one.val = 5.0;
Subclass<ParamOne, ParamTwo> test;
test.Method(one);
return 0;
}

variable parameter function, how to make it type safe and more meaningful?

I am a newer for C++, and my first language is Chinese, so my words with English may be unmeaningful, say sorry first.
I know there is a way to write a function with variable parameters which number or type maybe different each calling, we can use the macros of va_list,va_start and va_end. But as everyone know, it is the C style. When we use the macros, we will lose the benefit of type-safe and auto-inference, then I try do it whit C++ template. My work is followed:
#include<iostream>
#include<vector>
#include<boost/any.hpp>
struct Argument
{
typedef boost::bad_any_cast bad_cast;
template<typename Type>
Argument& operator,(const Type& v)
{
boost::any a(v);
_args.push_back(a);
return *this;
}
size_t size() const
{
return _args.size();
}
template<typename Type>
Type value(size_t n) const
{
return boost::any_cast<Type>(_args[n]);
}
template<typename Type>
const Type* piont(size_t n) const
{
return boost::any_cast<Type>(&_args[n]);
}
private:
std::vector<boost::any> _args;
};
int sum(const Argument& arg)
{
int sum=0;
for(size_t s=0; s<arg.size(); ++s)
{
sum += arg.value<int>(s);
}
return sum;
}
int main()
{
std::cout << sum((Argument(), 1, 3, 4, 5)) << std::endl;
return 0;
}
I think it's ugly, I want to there is a way to do better? Thanks, and sorry for language errors.
You can do something like this:
template <typename T>
class sum{
T value;
public:
sum ()
: value() {};
// Add one argument
sum<T>& operator<<(T const& x)
{ value += x; return *this; }
// to get funal value
operator T()
{ return value;}
// need another type that's handled differently? Sure!
sum<T>& operator<<(double const& x)
{ value += 100*int(x); return *this; }
};
#include <iostream>
int main()
{
std::cout << (sum<int>() << 5 << 1 << 1.5 << 19) << "\n";
return 0;
}
Such technique (operator overloading and stream-like function class) may solve different problems with variable arguments, not only this one. For example:
create_window() << window::caption - "Hey" << window::width - 5;
// height of the window and its other parameters are not set here and use default values
After giving it some thought, I found a way to do it using a typelist. You don't need an any type that way, and your code becomes type-safe.
It's based on building a template structure containing a head (of a known type) and a tail, which is again a typelist. I added some syntactic sugar to make it more intuitive: use like this:
// the 1 argument processing function
template< typename TArg > void processArg( const TArg& arg ) {
std::cout << "processing " << arg.value << std::endl;
}
// recursive function: processes
// the first argument, and calls itself again for
// the rest of the typelist
// (note: can be generalized to take _any_ function
template< typename TArgs >
void process( const TArgs& args ) {
processArg( args.head );
return process( args.rest );
}
template<> void process<VoidArg>( const VoidArg& arg ){}
int main() {
const char* p = "another string";
process( (arglist= 1, 1.2, "a string", p ) );
}
And here is the argument passing framework:
#include <iostream>
// wrapper to abstract away the difference between pointer types and value types.
template< typename T > struct TCont {
T value;
TCont( const T& t ):value(t){}
};
template<typename T, size_t N> struct TCont< T[N] > {
const T* value;
TCont( const T* const t ) : value( t ) { }
};
template<typename T> struct TCont<T*> {
const T* value;
TCont( const T* t ): value(t){}
};
// forward definition of type argument list
template< typename aT, typename aRest >
struct TArgList ;
// this structure is the starting point
// of the type safe variadic argument list
struct VoidArg {
template< typename A >
struct Append {
typedef TArgList< A, VoidArg > result;
};
template< typename A >
typename Append<A>::result append( const A& a ) const {
Append<A>::result ret( a, *this );
return ret;
}
//syntactic sugar
template< typename A > typename Append<A>::result operator=( const A& a ) const { return append(a); }
} const arglist;
// typelist containing an argument
// and the rest of the arguments (again a typelist)
//
template< typename aT, typename aRest >
struct TArgList {
typedef aT T;
typedef aRest Rest;
typedef TArgList< aT, aRest > Self;
TArgList( const TCont<T>& head, const Rest& rest ): head( head ), rest( rest ){}
TCont<T> head;
Rest rest;
template< typename A > struct Append {
typedef TArgList< T, typename Rest::Append<A>::result > result;
};
template< typename A >
typename Append< A >::result append( const A& a ) const {
Append< A >::result ret ( head.value, (rest.append( a ) ) );
return ret;
}
template< typename A > typename Append<A>::result operator,( const A& a ) const { return append(a); }
};