Nice little exercise

I ran into this during my own programming. I just wanted to share it with you because it looks interesting. Not too hard, but a nice mental exercise for those who like this sort of thing.

Exercise: Write a helper function to capture the common code shown below.

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>

template <int> struct AComponent;  template <int> struct BComponent;

struct A {};
struct B {};

struct Visitor {
	template <int N> void visit (AComponent<N>*) const {std::cout << "AComponent<" << N << "> visited.\n";}
	template <int N> void visit (BComponent<N>*) const {std::cout << "BComponent<" << N << "> visited.\n";}
	template <int N> void createComponents (int, A*) const;
	template <int N> void createComponents (int, B*) const;
};

template <int N> 
struct AComponent {
	AComponent(const A*) {}
	void accept (const Visitor& visitor) {visitor.visit(this);}
};

template <int N> 
struct BComponent {
	BComponent(const B*) {}
	void accept (const Visitor& visitor) {visitor.visit(this);}
};

template <int N>
void Visitor::createComponents (int n, A* a) const {
	if (n == N) {
		AComponent<N>* component = new AComponent<N>(a);
		// Code X
		component->accept(*this);
		// Code Y
		return;
	}
	createComponents<N+1> (n, a);
}

template <> void Visitor::createComponents<20> (int, A*) const {}  // End of recursion

template <int N>
void Visitor::createComponents (int n, B* b) const {
	if (n == N) {
		BComponent<N>* component = new BComponent<N>(b);
		// Code X
		component->accept(*this);
		// Code Y
		return;
	}
	createComponents<N+1> (n, b);
}

template <> void Visitor::createComponents<20> (int, B*) const {}  // End of recursion

int main() {
	A* a = new A;
	B* b = new B;
	Visitor visitor;
	const int num = 5;  // some random number
	visitor.createComponents<0>(num, a);  // AComponent<5> visited.
	visitor.createComponents<0>(num, b);  // BComponent<5> visited.
}


I'll post my solution in one hour. Perhaps your solution will be better than mine.
Last edited on
Here's my solution, though there might be a better one:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>

template <int> struct AComponent;  template <int> struct BComponent;

struct A {
	template <int N> using component_type = AComponent<N>;  // *** Added
};

struct B {
	template <int N> using component_type = BComponent<N>;  // *** Added
};

struct Visitor {
  private:
	template<int, typename> class CreateComponents;  // *** Added
  public:
	template <int N> void visit (struct AComponent<N>*) const {std::cout << "AComponent<" << N << "> visited.\n";}
	template <int N> void visit (struct BComponent<N>*) const {std::cout << "BComponent<" << N << "> visited.\n";}
	template <int N, typename T> void createComponents (int n, const T* t) const {  // *** Modified.  All overloads expressed as a single template function with T now.
		CreateComponents<N,T>(*this)(n, t);  // Must pass *this into the CreateComponents<N,T> constructor.
	}
};

template <int N>
struct AComponent {
	AComponent(const A*) {}
	void accept (const Visitor& visitor) {visitor.visit(this);}
};

template <int N>
struct BComponent {
	BComponent(const B*) {}
	void accept (const Visitor& visitor) {visitor.visit(this);}
};

template<int N, typename T>
class Visitor::CreateComponents : public CreateComponents<N + 1, T> {
  private:
	const Visitor& visitor;
  public:
	CreateComponents (const Visitor& v) : CreateComponents<N + 1, T>(v), visitor(v) {}
	void operator()(int n, const T* t) const {
		if (n == N) {
			typename T::template component_type<N>* component = new typename T::template component_type<N>(t);  // Template disambiguator needed with GCC 4.8.1.
			// Code X
			component->accept(visitor);  // accept visitor instead of *this now.
			// Code Y
			return;
		}
		CreateComponents<N + 1, T>::operator()(n, t);
	}
};

template<typename T>
class Visitor::CreateComponents<20, T> {  // Because partial specialization of a member function is not allowed, the function object Visitor::CreateComponents is needed.
  private:
	const Visitor& visitor;
  public:
	CreateComponents (const Visitor& v) : visitor(v) {}
	void operator()(int, const T*) const {}  // End of recursion
};

int main() {
	A* a = new A;
	B* b = new B;
	Visitor visitor;
	const int num = 5;  // some random number
	visitor.createComponents<0>(num, a);  // AComponent<5> visited.
	visitor.createComponents<0>(num, b);  // BComponent<5> visited.
}
Last edited on
Here's another one. Capture the common code here with a helper function:

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
33
#include <iostream>

struct Base {
    virtual ~Base() = default;
    void bar (int n) const {std::cout << "bar " << n << std::endl;}
    virtual void foo() const {
    	// Code X
	    for (int n = 0;  n < 5;  n++) {
	    	// Code Y
	        bar(n);
	    }
	    // Code Z
    }
};

struct Derived : Base {
    void baz (double d, int n) const {std::cout << "baz " << d << ' ' << n << std::endl;}
    virtual void foo() const override {
    	// Code X
	    for (int n = 0;  n < 5;  n++) {
	    	// Code Y
	        baz(4.5, n);
	    }
	    // Code Z
    }
};

int main() {
    Base b;
    Derived d;
    b.foo();
    d.foo();
}
Use pointer to data members:

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
#include <iostream>

struct Base {
    virtual ~Base() = default;
    void bar (int n) {std::cout << "bar " << n << std::endl;}
    void foo() {helper(&Base::bar);}
  protected:
  	template <typename T, typename... Args, typename R> void helper (R T::*, Args...);
};

struct Derived : Base {
    void baz (double d, int n) {std::cout << "baz " << d << ' ' << n << std::endl;}
    void foo() {helper<Derived, double> (&Derived::baz, 4.5);}
};

template <typename T, typename... Args, typename R>
void Base::helper (R T::*f, Args... args) {
    // Code X
    for (int n = 0;  n < 5;  n++) {
    	// Code Y
        (dynamic_cast<T*>(this)->*f)(args..., n);
    }
    // Code Z
}

int main() {
    Base b;
    Derived d;
    b.foo();
    d.foo();
}


At first I tried using
1
2
3
4
5
6
7
8
9
template <typename T, typename... Args>
void Base::helper (void (T::*f)(int, Args...) const, Args... args) const {
    // Code X
    for (int n = 0;  n < 5;  n++) {
    	// Code Y
        (dynamic_cast<const T*>(this)->*f)(n, args...);
    }
    // Code Z
}

But the order of the parameters of Derived::baz needed to be changed because the parameter pack args... must go at the end, not the beginning.
This solution thus causes a lot of headache if Derived::baz has already been used at many places in the program.
Last edited on
Topic archived. No new replies allowed.