Code consolidation and redundancy involving templates and vectors

Greetings all... Below I have a sample program with the headers and cpp files listed. As you can see in the main routine, there are 5 function calls that are all very similar. They call 5 corresponding routines in the Classrooms class. Can I solicit solutions to making this one call, with variables or templates to reduce the calls, or at least have a single call with typing? The only thing I can't change is the access of the vectors in the Classrooms.h.

I've tried this with templates and to be honest damn near threw my keyboard through the monitor with frustration. Anybody can help?


Names.h

1
2
3
4
5
    class Names {
        public:

            enum class Enums : int { Jack, Jill, John, Judy, Jzee };
    };


Names.cpp
 
    #include "Names.h" 


Students.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    #include <algorithm>
    #include <condition_variable>
    #include "Names.h"

    template <Names::Enums name_a> class Students {
        public:
            Students();
            void PrintEnum();
    };

    template <Names::Enums name_a> Students<name_a>::Students() {
    }

    template <Names::Enums name_a> void Students<name_a>::PrintEnum() {
        printf("%s: Enum value for name = %d\n", __func__, (int) name_a);
    }


Students.cpp
 
    #include "Students.h" 


Classrooms.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    #include <algorithm>
    #include <condition_variable>
    #include "Names.h"
    #include "Students.h"

    class Classrooms {
        private:
            std::vector<std::unique_ptr<Students<Names::Enums::Jack>>> Jacks;
    
            std::vector<std::unique_ptr<Students<Names::Enums::Jill>>> Jills;
    
            std::vector<std::unique_ptr<Students<Names::Enums::John>>> Johns;
    
            std::vector<std::unique_ptr<Students<Names::Enums::Judy>>> Judys;
    
            std::vector<std::unique_ptr<Students<Names::Enums::Jzee>>> Jzees;

        public:

            Classrooms() { }
    
            Students<Names::Enums::Jack>* Get_a_Jack(int e) { return Jacks.at(e).get(); }
    
            Students<Names::Enums::Jill>* Get_a_Jill(int e) { return Jills.at(e).get(); }
    
            Students<Names::Enums::John>* Get_a_John(int e) { return Johns.at(e).get(); }
    
            Students<Names::Enums::Judy>* Get_a_Judy(int e) { return Judys.at(e).get(); }
    
            Students<Names::Enums::Jzee>* Get_a_Jzee(int e) { return Jzees.at(e).get(); }
    
    };


Classrooms.cpp
 
    #include "Classrooms.h" 


main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    #include <algorithm>
    #include <condition_variable>
    #include "Names.h"
    #include "Classrooms.h"

    using namespace std;

    int main(int argc, char** argv) {
    
        Classrooms *cr = new Classrooms();
    
        cr->Get_a_Jack(0)->PrintEnum();
    
        cr->Get_a_Jill(0)->PrintEnum();
    
        cr->Get_a_John(0)->PrintEnum();
    
        cr->Get_a_Judy(0)->PrintEnum();
    
        cr->Get_a_Jzee(0)->PrintEnum();

        return 0;
    }
Last edited on
Since line 12 in main.cpp is going to throw an exception, I suppose you could just skip the subsequent function calls.

The only thing I can't change is the access of the vectors in the Classrooms.h.
And by this you mean... ?

Your design is very odd. I would expect a type named Names to be a collection of names, not a type masquerading as a namespace. I would expect a type named Students to represent a collection of students, not a single student parameterized on a particular value. I would expect a type named Classrooms to be represent multiple classrooms, not be a collection of containers of different types of students.

Based on what I see, my suggestion would be that the Students type should not be templated and that the Classrooms type should be refactored based on the changes to the Students type. (And better names should be chosen for the types.)
Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <vector>

enum class name { First, Jack=First, Jill, John, Judy, Jzee, Null };

constexpr name next( name n ) { return name( int(n) + 1 ) ; }

std::ostream& operator<< ( std::ostream& stm, name n )
{
    static const char* const description[] = { "Jack", "Jill", "John", "Judy", "Jzee", "" } ;
    return stm << description[ int(n) ] ;
}

template < name N > struct student
{
    friend std::ostream& operator<< ( std::ostream& stm, student<N> )
    { return stm << "student { name::" << N << " }" ; }
};

template < name N > struct vector_builder : vector_builder< next(N) >
{ std::vector< student<N> > students ; };

template <> struct vector_builder<name::Null> {} ;

struct classroom : private vector_builder<name::First>
{
    template < name N > auto& get() { return vector_builder<N>::students ; }
    template < name N > const auto& get() const { return vector_builder<N>::students ; }
};

http://coliru.stacked-crooked.com/a/58c24944852c4b6e
Cire and JLBorges

Thanks for the inputs... the sample code I presented in my post is only an example and not the real code. The actual code is much too complicated and expansive and sensitive to put on a post. All I am looking for is a possible solution, based on the example, of a way to consolidate the calls given the structures of the vectors and the fact that they're private in the Classrooms.h

Please don't focus on the classroom, student name thing. That's not the relevant part.
Last edited on

Very nice solution...

template < Names::Enums Name >
Students<Name>* Get(int e)
{
return std::get< std::vector<std::unique_ptr<Students<Name>>> >( std::tie( Jacks, Jills, Johns, Judys, Jzees ) ).at(e).get();
}
Topic archived. No new replies allowed.