1

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.

Hooked
  • 84,485
  • 43
  • 192
  • 261

2 Answers2

5

Update:

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

Original answer text:

#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();
sehe
  • 374,641
  • 47
  • 450
  • 633
  • See it **[live on Coliru](http://coliru.stacked-crooked.com/view?id=5b3f9917ae6db3bcbe4f85452a781e20-93e6c6235a92d0c233f44beab03470ad)** – sehe Aug 19 '13 at 21:18
  • Neat! Though I don't fully understand everything that's going on in the template def., especially the parts about `remove_reference` and `forward`. – Hooked Aug 19 '13 at 21:19
  • @Hooked Forwarding is what makes it possible to do e.g. `set_parameter((X()), 3.14, &X::foo)`, and enjoy _move semantics_ on the assigned values. The `remove_reference` is to help deduction, because for `lvalues`, `T` would be deduced to `X&`, but `T2` _needs_ to be unqualified. – sehe Aug 19 '13 at 21:22
  • @MooingDuck I think I changed that iffy bit around :/ Added a ... use-case to demonstrate when it could be "useful" (in this case in order to `return 11;` from `main` in a more obfuscated manner) – sehe Aug 19 '13 at 21:34
  • @Hooked I missed the part about iterating your container. Fixed that. **[Live on Coliru](http://coliru.stacked-crooked.com/view?id=5ee84ff2960cb9c079ddf6d90559a5dd-93e6c6235a92d0c233f44beab03470ad)** once again. – sehe Aug 19 '13 at 21:49
0

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);
Tomek
  • 4,554
  • 1
  • 19
  • 19
  • This isn't going to deduce `T`. Also, if you are assigning with any implicit conversion, it isn't going to deduce `N` either. – sehe Aug 19 '13 at 21:17