1

Consider a class:

class A
{
    int attrib1, attrib2, attrib3;
    double attrib4;
    std::string attrib5;
    int attrib6, attrib7, attrib8, attrib9, attrib10;

public:
    // Member functions.
}

Each attrib variables represent different member variables for various usage and usually related to each other (It might break if there are some mismatches of the values).

For example, I need to access and get the value of attrib4, attrib5, attrib6, attrib7, attrib8, attrib9, attrib10. To keep the class encapsulated, I must create multiple member functions just to get the variable content or set the individual variable. This is the reason for an idea of a single function to return specific variables by parameter.

I had an idea to use the template for this purpose. First, I created an enum for variable names:

enum class Variable
{
    attrib4,
    attrib5,
    attrib6, attrib7, attrib8, attrib9, attrib10,
};

Then, I created a member function template in class A as follow:

template<typename T>
T get(Variable var)
{
    switch (var)
    {
    case Variable::attrib4:
        return static_cast<T>(attrib4);
    case Variable::attrib5:
        return static_cast<T>(attrib5);
    case Variable::attrib6:
        return static_cast<T>(attrib6);
    case Variable::attrib7:
        return static_cast<T>(attrib7);
    case Variable::attrib8:
        return static_cast<T>(attrib8);
    case Variable::attrib9:
        return static_cast<T>(attrib9);
    case Variable::attrib10:
        return static_cast<T>(attrib10);
    default:
        return 0;
    }
}

To use it, I must explicitly specify the variable types as the function cannot automatically deduce the type:

A a;
a.get<std::string>(Variable::attrib5);

The problem of this approach is that this function is error-prone and may be misused (or cannot work at all due to invalid cross conversion).

Are there any solutions for this idea?

A_72_
  • 19
  • 4

2 Answers2

0

You can use a pointer-to-member, eg:

template<typename T>
T& get(T (A::*member))
{
    return this->*member;
}

...

A a;
a.get(&A::attrib5);

However, since your members are private, this may not work exactly as shown. In which case, you can use the trick described in the following:

Can I access private members from outside the class without using friends?

Many of the answers to that are based on these blog articles:

Access to private members. That's easy!

Access to private members: Safer nastiness

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

If you need to access several related variables, you can define them as a set in the form of a struct.

#include <string>

struct Set1 {
    double attrib4;
    std::string attrib5;
    int attrib10;
};

class A {
    int attrib1, attrib2, attrib3;
    double attrib4;
    std::string attrib5;
    int attrib6, attrib7, attrib8, attrib9, attrib10;

public:
    Set1 getSet1() const {
        return {
            .attrib4 = attrib4,
            .attrib5 = attrib5,
            .attrib10 = attrib10
        };
    }

    void setSet1(Set1 set1) {
        attrib4 = set1.attrib4;
        attrib5 = std::move(set1.attrib5); // move optimization for string
        attrib10 = set1.attrib10;
    }
};

By using the designator syntax to initialize the structs ({.member = value} instead of just {value}) you reduce the chance for typos.

#include <iostream>

int main() {
    A a;
    a.setSet1({.attrib4 = 4.2, .attrib5 = "forty-two", .attrib10 = 42});

    auto set1 = a.getSet1();
    std::cout
        << set1.attrib4 << ", "
        << set1.attrib5 << ", "
        << set1.attrib10 << '\n';
}

If you don't use the extra safety of the designator syntax, you can just use a std::tuple instead of a struct.

#include <string>
#include <tuple>
#include <iostream>

class A {
    int attrib1, attrib2, attrib3;
    double attrib4;
    std::string attrib5;
    int attrib6, attrib7, attrib8, attrib9, attrib10;

public:
    std::tuple<double, std::string, int> getSet1() const {
        return std::make_tuple(attrib4, attrib5, attrib10);
    }

    void setSet1(double attrib4, std::string attrib5, int attrib10) {
        this->attrib4 = attrib4;
        this->attrib5 = std::move(attrib5); // move optimisation for string
        this->attrib10 = attrib10;
    }
};

int main() {
    A a;
    a.setSet1(4.2, "forty-two", 42);

    auto [attrib4, attrib5, attrib10] = a.getSet1(); // structured binding
    std::cout << attrib4 << ", " << attrib5 << ", " << attrib10 << '\n';
}
Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51