Reputation: 88198
Reading through previous answers, I feel like I may have a design problem, but even if the answer is academic I'd still like to know if it is possible. I've been programming in Python for awhile and it shows. I'm trying to create something like a setattr
access on an object. By hand it looks like:
template<class T, class U>
void set_parameter_sigma(T &S, U val) {
for(auto &x: S) { x.sigma = val; }
}
template<class T, class U>
void set_parameter_epsilon(T &S, U val) {
for(auto &x: S) { x.epsilon = val; }
}
template<class T, class U>
void set_parameter_length(T &S, U val) {
for(auto &x: S) { x.length = val; }
}
What I'd like is something that looks like the following pseudocode:
template<class T, class U, class N>
void set_parameter(T &S, U val) {
for(auto &x: S) { x.N = val; }
}
I could call it like set_parameter(foo, 2.0, "epsilon")
and the compiler would create the set_parameter_epsilon
function automagically. While I'm sure the boost can do this, I'd prefer to see a STL-only version if possible.
Upvotes: 1
Views: 723
Reputation: 393457
Oops Turned out I had missed the requirement to loop on container elements inside the setter. Well, then, let me amend my mistake:
#include <utility>
template <class C, class U, class U2 /* assignable from U*/,
class T = typename C::value_type>
void set_parameter(C& container, U&& val, U2 (T::* pmember), typename C::value_type* sfinae=nullptr)
{
for (auto& instance : container)
(instance.*(pmember)) = std::forward<U>(val);
}
#include <iostream>
#include <string>
#include <vector>
struct X
{
double foo;
std::string splurgle;
};
int main()
{
std::vector<X> xs(10);
set_parameter(xs, 42, &X::foo);
set_parameter(xs, "hello world", &X::splurgle);
for (auto const& x : xs)
std::cout << x.foo << ", " << x.splurgle << "\n";
}
Which prints (Live on Coliru)
42, hello world
42, hello world
42, hello world
42, hello world
42, hello world
42, hello world
42, hello world
42, hello world
42, hello world
42, hello world
#include <utility>
#include <type_traits>
template <class T, class U, class U2 /* assignable from U*/, class T2 = typename std::remove_reference<T>::type>
T&& set_parameter(T&& instance, U&& val, U2 (T2::* pmember))
{
(instance.*(pmember)) = std::forward<U>(val);
return std::forward<T>(instance);
}
This is littered with nuances. But suffice it to say, it "works" as requested:
#include <iostream>
#include <string>
struct X
{
double foo;
std::string splurgle;
};
int main()
{
X x;
set_parameter(x, 3.14 , &X::foo);
set_parameter(x, "hello world", &X::splurgle);
std::cout << x.foo << ", " << x.splurgle;
}
Output:
3.14, hello world
For added insanity: Note that by returning a useful value you can do more ... interesting things, still:
return set_parameter(
set_parameter(X(), 3.14, &X::foo),
"hello world", &X::splurgle)
.splurgle.length();
Upvotes: 5
Reputation: 4659
You probably can use pointer to member for this but I am not sure if you are going to like that:
struct XX
{
char c;
int i;
};
template<class T, class U, class N>
void set_parameter(T &S, U val, N T::*n) {
for(auto &x: S) { x.*n = val; }
}
set_parameter(..., ..., &XX::c);
set_parameter(..., ..., &XX::i);
Upvotes: 0