You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
5.4 KiB
213 lines
5.4 KiB
#include <catch2/catch_test_macros.hpp>
|
|
#include <fmt/core.h>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <cstdlib>
|
|
|
|
using namespace fmt;
|
|
|
|
struct Counts {
|
|
int default_constructor = 0;
|
|
int value_constructor = 0;
|
|
int copy_constructor = 0;
|
|
int move_constructor = 0;
|
|
int move_assign = 0;
|
|
int copy_assign = 0;
|
|
int destructor = 0;
|
|
|
|
void reset() {
|
|
default_constructor = 0;
|
|
value_constructor = 0;
|
|
copy_constructor = 0;
|
|
move_constructor = 0;
|
|
move_assign = 0;
|
|
copy_assign = 0;
|
|
destructor = 0;
|
|
}
|
|
|
|
int totals() {
|
|
return default_constructor +
|
|
value_constructor +
|
|
copy_constructor +
|
|
move_constructor +
|
|
move_assign +
|
|
copy_assign +
|
|
destructor;
|
|
}
|
|
|
|
void dump() {
|
|
fmt::println("default_constructor {} value_constructor {} copy_constructor {} move_constructor {} move_assign {} copy_assign",
|
|
default_constructor,
|
|
value_constructor,
|
|
copy_constructor,
|
|
move_constructor,
|
|
move_assign,
|
|
copy_assign,
|
|
destructor);
|
|
}
|
|
};
|
|
|
|
Counts COUNTS;
|
|
|
|
// Some heavy object
|
|
struct A {
|
|
int val = 0;
|
|
|
|
A() {
|
|
COUNTS.default_constructor++;
|
|
std::cout << "default called constructor with: " << val << std::endl;
|
|
}
|
|
|
|
A(int val) : val(val) {
|
|
COUNTS.value_constructor++;
|
|
std::cout << "called constructor with: " << val << std::endl;
|
|
}
|
|
|
|
A(A const& other) {
|
|
COUNTS.copy_constructor++;
|
|
std::cout << "calling copy(A const& other): " << val << std::endl;;
|
|
}
|
|
A(A&& other) {
|
|
COUNTS.move_constructor++;
|
|
std::cout << "calling move(A&& other): " << val << std::endl;;
|
|
}
|
|
|
|
A& operator=(A&& other) {
|
|
COUNTS.move_assign++;
|
|
std::cout << "calling move(A&& other)=: " << val << std::endl;;
|
|
return *this;
|
|
}
|
|
|
|
A& operator=(A const& other) {
|
|
COUNTS.copy_assign++;
|
|
std::cout << "calling copy(A const& other)=: " << val << std::endl;;
|
|
return *this;
|
|
}
|
|
|
|
~A() {
|
|
COUNTS.destructor++;
|
|
std::cout << "destructor called" << std::endl;
|
|
}
|
|
};
|
|
|
|
|
|
TEST_CASE("emplace move tests", "[emplace]") {
|
|
A a{10};
|
|
std::vector<A> test;
|
|
test.reserve(10); // to prevent vector resizing and creating new objects on the way
|
|
std::cout << "===== Emplacing Start =====\n";
|
|
// pass by l-value and will call copy constructor since it'll match
|
|
// A(A const&)
|
|
std::cout << "== 1. Emplace: Calling copy constructor \n";
|
|
test.emplace_back(a);
|
|
|
|
// pass by r-value and will call move constructor since it'll match
|
|
// A(A&&)
|
|
std::cout << "== 2. Emplace: Calling move constructor \n";
|
|
test.emplace_back(std::move(a));
|
|
|
|
std::cout << "== 3. Emplace: Calling move constructor without direct constructor\n";
|
|
test.emplace_back(100);
|
|
|
|
// pass by pr-value (pure r-value which is those value that has not come to an existance yet)
|
|
// and will call move constructor since it'll match A(A&&).
|
|
// "copy-elision" could be applied here but I don't know why compilers
|
|
// refused to do it. Maybe how vectors are implemented and you need
|
|
// to put the value in a slot so the compiler had to call the move constructor
|
|
std::cout << "== 4. Emplace: Calling move constructor\n";
|
|
test.emplace_back(A{});
|
|
}
|
|
|
|
TEST_CASE("pushback move tests", "[push_back]") {
|
|
std::cout << "\n\n!!!!!!!!!! PUSH BACK alternative !!!!!!!!!!\n";
|
|
|
|
A a{20};
|
|
std::vector<A> test;
|
|
test.reserve(10); // to prevent vector resizing and creating new objects on the way
|
|
std::cout << "===== Push Back Start =====\n";
|
|
// pass by l-value and will call copy constructor since it'll match
|
|
// A(A const&)
|
|
std::cout << "== 1. Push Back: Calling copy constructor \n";
|
|
test.push_back(a);
|
|
|
|
// pass by r-value and will call move constructor since it'll match
|
|
// A(A&&)
|
|
std::cout << "== 2. Push Back: Calling move constructor \n";
|
|
test.push_back(std::move(a));
|
|
|
|
std::cout << "== 3. Push Back: Calling move constructor without direct constructor\n";
|
|
test.push_back(200);
|
|
|
|
// pass by pr-value (pure r-value which is those value that has not come to an existance yet)
|
|
// and will call move constructor since it'll match A(A&&).
|
|
// "copy-elision" could be applied here but I don't know why compilers
|
|
// refused to do it. Maybe how vectors are implemented and you need
|
|
// to put the value in a slot so the compiler had to call the move constructor
|
|
std::cout << "== 4. Push Back: Calling move constructor\n";
|
|
test.push_back(A{});
|
|
|
|
}
|
|
|
|
|
|
TEST_CASE("proof you don't need emplace or std::move", "[proof]") {
|
|
A a{20};
|
|
A b{30};
|
|
COUNTS.reset();
|
|
|
|
std::vector<A> emtest;
|
|
emtest.reserve(10);
|
|
|
|
std::vector<A> pbtest;
|
|
pbtest.reserve(10);
|
|
REQUIRE(COUNTS.totals() == 0);
|
|
|
|
emtest.emplace_back(a);
|
|
REQUIRE(COUNTS.totals() == 1);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
|
|
pbtest.push_back(b);
|
|
REQUIRE(COUNTS.totals() == 1);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
|
|
// confirm these do the same number of calls
|
|
emtest.emplace_back(A{});
|
|
REQUIRE(COUNTS.totals() == 3);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
|
|
pbtest.push_back(A{});
|
|
REQUIRE(COUNTS.totals() == 3);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
|
|
{
|
|
REQUIRE(COUNTS.totals() == 0);
|
|
|
|
A c{};
|
|
emtest.emplace_back(std::move(c));
|
|
REQUIRE(COUNTS.totals() == 2);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
|
|
A d{};
|
|
pbtest.push_back(std::move(d));
|
|
REQUIRE(COUNTS.totals() == 2);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
}
|
|
|
|
COUNTS.reset();
|
|
|
|
emtest.emplace_back(200);
|
|
REQUIRE(COUNTS.totals() == 1);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
|
|
pbtest.push_back(200);
|
|
REQUIRE(COUNTS.totals() == 3);
|
|
COUNTS.dump();
|
|
COUNTS.reset();
|
|
}
|
|
|