go to start Ex W4
|home |print view |recent changes |changed March 16, 2017 |
exact
|You are 54.81.59.211 <- set your identity!

Sections: !BoundedBuffer | Type Deduction | Perfect Forwarding | a) Forwarding Factory Function with One Single Argument | b) Factory function with Multiple Arguments |

BoundedBuffer ^

If you have not done it yet, finish the BoundedBuffer from the Testat exercise ExW3 first. Remember the hand-in deadline.

Type Deduction ^

In the lecture you have been taught about type deduction for function templates (and auto), e.g. in source code as follows:

template<typename T>
void f(T && t) {...}

When the template above is instanciated, the type T is deduced, which also implies the specific type of the parameter t. This deduction follows specific rules, depending on the declared template parameter type (above T&&). If you don't recall them exactly, have a look at the corresponding slides. If you have trouble understanding the rules, discuss them with your colleagues or ask your supervisor.

When you are confident you can play a game we have prepared for you. It's a console-based trivia game which asks you about ten specific cases with a given function template declaration and a corresponding call. You will be asked about the deduced type of T and t.

Here are the sources of the game. Create a new empty C++ project (and add the boost library to the includes if necessary). Have fun!

Sources: https:files/TypeDeductionTrivia.zip

Remarks: You can have a look at the source code of the game. But, beware of the QuestionGenerator.cpp! There is some preprocessor magic in it, which might be tough to understand if you are unfamilliar with it. It was necessary for overcomming the lack of reflection in C++. Don't worry, we won't ask you to know about such stuff in the exam though.

Remarks 2.0: The cases are hardcoded, but the actual answers are retrieved from the cases (under the assumption your compiler and boost do a decent job in figuring them out). If you feel you might have missed some of the cases due to the random selection for the trivia, you can use the printAll function to see all questions with the correct answers.

Perfect Forwarding ^

Given the code for BoundedBuffer that you have implemented as Testat, we have two overloads of the push member function.

void push(T const &);
void push(T &&);

Question: We have an overload of the push member function with a parameter of type T&&. Why is T&& always an rvalue reference?

Note: There used to be an exercise to create a Forwarding Constructor, which actually is not a really good idea. Since the T&& overload is greedy. This results in copying non-const buffers taking prefering the Forwarding Constructor over the Copy Constructor, which does not work. Therefore, we've changed the exercise.

a) Forwarding Factory Function with One Single Argument ^

Create a factory function for your BoundedBuffer type that takes a one argument of the element type. It shall create the BoundedBuffer and add the element to the buffer. lvalue arguments have to be passed as lvalue to the push function and rvalues have to be passed as rvalues to the push function.

We recomment to create this make_buffer function to be implemented as static member function, as it is easier to specify the arguments of the BoundedBuffer to be created.

void test_make_bounded_buffer_from_rvalue_argument_contains_one_element() {
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{});
	ASSERT_EQUAL(1, buffer.size());
}

void test_make_bounded_buffer_from_rvalue_argument_object_moved() {
	MemoryOperationCounter expected{1, 0, true};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{});
	ASSERT_EQUAL(expected, buffer.front());
}

void test_bounded_buffer_constructed_with_lvalue_argument_object_copied() {
	MemoryOperationCounter expected{0, 1, true};
	MemoryOperationCounter insertee{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(insertee);
	ASSERT_EQUAL(expected, buffer.front());
}

void test_bounded_buffer_constructed_with_const_lvalue_argument_object_copied() {
	MemoryOperationCounter expected{0, 1, true};
	MemoryOperationCounter const insertee{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(insertee);
	ASSERT_EQUAL(expected, buffer.front());
}

Time for action: Your turn!

b) Factory function with Multiple Arguments ^

As statet in part a) of the exercise, we actually want to pass an arbitrary number of arguments to that factory function. This mixes the topics of perfect forwarding and variadic templates. We have not explicitly covered this in the lecture, but you are encouraged to figure out on your own how to achieve this.

Hint: Create a private helper member function push_many to push multiple elements to the BoundedBuffer. You need this function since it is easier to call a function recursively than a constructor.

void test_make_bounded_buffer_from_two_rvalue_arguments_contains_two_elements() {
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{}, MemoryOperationCounter{});
	ASSERT_EQUAL(2, buffer.size());
}

void test_make_bounded_buffer_from_two_lvalue_arguments_contains_two_elements() {
	MemoryOperationCounter element1{}, element2{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(element1, element2);
	ASSERT_EQUAL(2, buffer.size());
}

void test_make_bounded_buffer_from_too_many_elements_throws() {
	ASSERT_THROWS((BoundedBuffer<int, 1>::make_buffer(1, 2)), std::invalid_argument);
}

void test_make_bounded_buffer_from_two_rvalue_arguments_first_element_moved() {
	MemoryOperationCounter expected{1, 0, true};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{}, MemoryOperationCounter{});

	ASSERT_EQUAL(expected, buffer.front());
}

void test_make_bounded_buffer_from_two_rvalue_arguments_second_element_moved() {
	MemoryOperationCounter expected{1, 0, true};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{}, MemoryOperationCounter{});
	ASSERT_EQUAL(expected, buffer.back());
}

void test_make_bounded_buffer_from_two_rvalue_arguments_first_element_copied() {
	MemoryOperationCounter expected{0, 1, true};
	MemoryOperationCounter lvalue{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(lvalue, MemoryOperationCounter{});
	ASSERT_EQUAL(expected, buffer.front());
}

void test_make_bounded_buffer_from_two_mixed_arguments_second_element_moved() {
	MemoryOperationCounter expected{1, 0, true};
	MemoryOperationCounter lvalue{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(lvalue, MemoryOperationCounter{});
	ASSERT_EQUAL(expected, buffer.back());
}

void test_make_bounded_buffer_from_two_rvalue_arguments_second_element_copied() {
	MemoryOperationCounter expected{0, 1, true};
	MemoryOperationCounter lvalue{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{}, lvalue);
	ASSERT_EQUAL(expected, buffer.back());
}

void test_make_bounded_buffer_from_two_mixed_arguments_first_element_moved() {
	MemoryOperationCounter expected{1, 0, true};
	MemoryOperationCounter lvalue{};
	BoundedBuffer<MemoryOperationCounter, 15> buffer = BoundedBuffer<MemoryOperationCounter, 15>::make_buffer(MemoryOperationCounter{}, lvalue);
	ASSERT_EQUAL(expected, buffer.front());
}

If you struggle with the syntax for forwarding variadic parameters, you can have a look at SolW4.


|home |print view |recent changes |changed March 16, 2017 |
exact
|You are 54.81.59.211 <- set your identity!

Ex W4
go to start