Iterate over class members

Hi, I want to know if I can iterate class members so that I don't have writeElementfor every member of the class.

I would really like it to be in the for loop, so that it contains all open members.

My code is:

class Student
{
public:
    string name;
    string lastName;
    int age;
    string gender;
    vector<int> grades;

public:
    void read(istream& in)
    {
        readElement(in, name);
        readElement(in, lastName);
        readElement(in, age);
        readElement(in, gender);
        readElement(in, grades);
    }

    void write(ostream& out)
    {
        //add a loop here
        writeElement(out, name);
        writeElement(out, lastName);
        writeElement(out, age);
        writeElement(out, gender);
        writeElement(out, grades);
    }
};
+4
source share
2 answers

There is no easy way to achieve what you want. The static reflection proposed for C ++ 20 will make this possible.

You now have several (not large) options:

  • Manually write out the calls readElement/ writeElementas you did. You can avoid repetition by providing a higher order function and passing readElement/ writeElementas arguments (sort of like a visitor).

  • , .

  • std::tuple struct std::apply + variadic generic lambda "" .

  • , magic_get, (ab) , .


:

template <typename F>
void visit(F&& f)
{
    f(name);
    f(lastName);
    f(age);
    f(gender);
    f(grades);   
}

void read(istream& in)
{
    visit([&in](auto&& x){ readElement(in, x); });
}

void write(ostream& out)
{
    visit([&in](auto&& x){ writeElement(in, x); });
}

:

std::tuple
<
    string      /* name */,
    string      /* lastName */,
    int         /* age */,
    string      /* gender */,
    vector<int> /* grades */
> data;

template <typename F>
void visit(F&& f)
{
    std::apply([](auto&&... xs){ (f(xs), ...); }, data);
}
+4

, , ; , - . , , . , , (). ; ostream, istream , .

#include <iostream>
#include <string>
#include <utility>
#include <tuple>
#include <vector>

// ostream operator<< for vector<T>
template<class T>
std::ostream& operator<<( std::ostream& out, const std::vector<T>& v ) {
    out << "{ ";
    for( auto& a : v )
        out << a << ' ';
    out << '}';
    return out;
}

// istream operator>> for vector<T>
template<class T>
std::istream& operator>>( std::istream& in, std::vector<T>& v ) {
    int i;
    std::string line;
    std::getline( std::cin, line );
    std::istringstream iss( line );
    while( iss >> i ) {
        v.push_back( i );
    }
    return in;
}

// function templates & ostream operator<< for tuple<T>
template<std::size_t> struct int_ {};

template<class Tuple, size_t Pos>
std::ostream& print_tuple( std::ostream& out, const Tuple& t, int_<Pos> ) {
    out << std::get<std::tuple_size<Tuple>::value - Pos>( t ) << ' ';
    return print_tuple( out, t, int_<Pos - 1>() );
}

template<class Tuple>
std::ostream& print_tuple( std::ostream& out, const Tuple& t, int_<1> ) {
    return out << std::get<std::tuple_size<Tuple>::value - 1>( t );
}

template<class... Args>
std::ostream& operator<<( std::ostream& out, const std::tuple<Args...>& t ) {
    return print_tuple( out, t, int_<sizeof...(Args)>() );
}

// function templates & istream operator << for tuple<T>
template<class Tuple, size_t Pos>
std::istream& write_tuple( std::istream& in, Tuple& t, int_<Pos> ) {
    in >> std::get<std::tuple_size<Tuple>::value - Pos>( t );
    return write_tuple( in, t, int_<Pos - 1>() );
}

template<class Tuple>
std::istream& write_tuple( std::istream& in, Tuple& t, int_<1> ) {
    return in >> std::get<std::tuple_size<Tuple>::value - 1>( t );
}

template<class... Args>
std::istream& operator>>( std::istream& in, std::tuple<Args...>& t ) {
    return write_tuple( in, t, int_<sizeof...(Args)>() );
}
// --------------------------------------------------

// class proto type for friend operators
template<class... T>
class StudentInfo;

template<class... T>
std::ostream& operator<< <>( std::ostream& out, const StudentInfo<T...>& c );

template<class... T>
std::istream& operator>> <>( std::istream& in, StudentInfo<T...>& c );

//template<typename... Args>
template<class...Args>
class StudentInfo {
public
    std::tuple<Args...> members;


    explicit StudentInfo(Args&&... args ) {
        members = std::make_tuple<Args...>( std::move( args )... );
    } 

    const StudentInfo<Args...>& operator() ( Args&&... args ) {
        members = std::make_tuple<Args...>( std::forward<Args>( args )... );
        return *this;
    }

    const StudentInfo<Args...> operator() ( Args&&... args ) const {
        members = std::make_tuple<Args...>( std::forward<Args>( args )... );
        return *this;
    }

    template<Args...>
    friend std::ostream& operator<< <>(std::ostream& out, const StudentInfo<Args...>& c);

    template<Args...>
    friend std::istream& operator>> <>( std::istream& in, StudentInfo<Args...>& c );

    StudentInfo<Args...>& operator=( StudentInfo<Args...>& c ) {
        if ( members == c.members ) 
            return *this;
        members = c.members;
        return *this;
    }

};

template<class... T>
std::ostream& operator<< <>( std::ostream& out, StudentInfo<T...>& c ) {
    return out << c.members;
}

template<class... T>
std::istream& operator>> <>( std::istream& in, StudentInfo<T...>& c ) {
    return in >> c.members;
}

:

int main() {
    std::string first{ "Some" };
    std::string last{ "Day" };
    int age = 1000;
    std::string sex{ "Unknown" };
    std::vector<int> grades{ 99, 98, 97, 92, 89, 88 };

    // create student info
    StudentInfo< std::string, std::string, int,
                 std::string, std::vector<int> > 
        studentA( std::move(first), std::move(last), 
                  std::move(age), std::move(sex), 
                  std::move(grades)
        );

    // outstream student info
    std::cout << studentA << '\n';

    // reset temps
    first.clear();
    last.clear();
    age = 0;
    sex.clear();
    grades.clear();

    // create 2nd student & assign new information from user input
    StudentInfo< std::string, std::string, int,
                 std::string, std::vector<int> >
        studentB( std::move(first), std::move(last), 
                  std::move(age), std::move(sex), 
                  std::move(grades)
        );

    // Check to make sure it has empty fields
    std::cout << "Student B information\n" << studentB << '\n';

    // Now let enter some stuff from the console and populate studentB
    std::cout << "\nEnter the student information\n";
    std::cin >> studentB;

    // Let check studentB info
    std::cout << studentB << '\n';

    // Another step let check our assignment operator
    StudentInfo< std::string, std::string, int,
                 std::string, std::vector<int> >
        studentC( std::move( first ), std::move( last ),
                  std::move( age ), std::move( sex ),
                  std::move( grades ) 
        );

    // Check studentC it should be empty
    std::cout << "Student C information\n" << studentC << '\n';

    // Let set studentC to studentA;
    studentC = studentA;

    // Print C info
    std::cout << "Student C new information\n" << studentC << '\n';

    // Finally test out the operator()
    studentC( std::move( std::get<0>( studentB.members ) ),
              std::move( std::get<1>( studentB.members ) ),
              std::move( std::get<2>( studentB.members ) ),
              std::move( std::get<3>( studentB.members ) ),
              std::move( std::get<4>( studentB.members ) )
    );
    std::cout << studentC << '\n';


    std:cout << "\nPress any key and enter to quit.\n";
    std::cin.get();
    return 0;
}

, .

. , <T>. , , , std::vector<T>. , , , . ; vector, iostream , , . .

0

Source: https://habr.com/ru/post/1695706/


All Articles