/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define UNITTEST_DEBUG #include "lib/function/ob_function.h" #include "lib/container/ob_array.h" #include #include #include namespace oceanbase { namespace unittest { using namespace common; using namespace std; function::DebugRecorder &recorder = function::DebugRecorder::get_instance(); function::DefaultFunctionAllocator &default_allocator = function::DefaultFunctionAllocator::get_default_allocator(); class TestObFunction: public ::testing::Test { public: TestObFunction() {}; virtual ~TestObFunction() {}; virtual void SetUp() { recorder.reset(); }; virtual void TearDown() { ASSERT_EQ(default_allocator.total_alive_num, 0); }; private: // disallow copy DISALLOW_COPY_AND_ASSIGN(TestObFunction); }; // 以下为C++中目前(截止到C++20标准)支持的所有可能的调用形式 // 1,普通函数 int func1(double arg1, const float &arg2) { return (arg1 + arg2); } // 2,裸函数指针 int (*func2)(double, const float &) = func1; // 3, 类成员函数和静态成员函数 struct Example { // 3.1 类成员函数 int func3(double arg1, const float &arg2) { return (arg1 + arg2); } // 3.2 静态成员函数 static int func4(double arg1, const float &arg2) { return (arg1 + arg2); } }; // 4, 仿函数 class Func5 { public: int operator()(double arg1, const float &arg2) { return (arg1 + arg2) * ratio; } private: static constexpr int ratio = 1;// 成员状态将影响调用过程 } func5; // 5, lambda表达式 auto func6 = [](double arg1, const float &arg2) -> int { return (arg1 + arg2); }; TEST_F(TestObFunction, lvalue_implicit_construction) { ObArray> func_array; ASSERT_EQ(OB_SUCCESS, func_array.push_back(func1)); ASSERT_EQ(OB_SUCCESS, func_array.push_back(func2)); ASSERT_EQ(OB_SUCCESS, func_array.push_back(&Example::func4)); ASSERT_EQ(OB_SUCCESS, func_array.push_back(func5)); ASSERT_EQ(OB_SUCCESS, func_array.push_back(func6)); float temp = 1.0f; for (int64_t idx = 0; idx < func_array.count(); ++idx) { ObFunction func = func_array.at(idx); ASSERT_EQ(3, func(2.0, temp)); } } TEST_F(TestObFunction, rvalue_implicit_construction) { ObArray> func_array; ASSERT_EQ(OB_SUCCESS, func_array.push_back([](double a, const float& b) -> int { return a + b; })); auto create_rvalue_function = []() { return Func5(); }; ASSERT_EQ(OB_SUCCESS, func_array.push_back(create_rvalue_function())); float temp = 1.0f; for (int64_t idx = 0; idx < func_array.count(); ++idx) { ObFunction func = func_array.at(idx); ASSERT_EQ(3, func(2.0, temp)); } } TEST_F(TestObFunction, perfect_forwarding) { float temp = 1.0f; ObFunction func = [](float &a) -> float* { return &a; }; ASSERT_EQ(&temp, func(temp)); } TEST_F(TestObFunction, construction) { ObFunction f1 = [](){ return 1; }; ObFunction f2 = f1; ObFunction f3((ObFunction(f1))); } class TestAllocator1 : public ObIAllocator { public: void *alloc(int64_t size) override { UNUSED(size); return nullptr; } void* alloc(const int64_t size, const ObMemAttr &attr) override { UNUSEDx(size, attr); return nullptr; } void free(void *ptr) override { UNUSED(ptr); } } test_allocator1; class TestAllocator2 : public ObIAllocator { public: void *alloc(int64_t size) override { return ob_malloc(size, ""); } void* alloc(const int64_t size, const ObMemAttr &attr) override { UNUSED(attr); return alloc(size); } void free(void *ptr) override { UNUSED(ptr); } } test_allocator2; TEST_F(TestObFunction, alloc) { float temp = 1.0f; // implicit construct then move construct ObFunction f1 = [](float &a) -> float* { return &a; }; ASSERT_EQ(true, f1.is_valid()); ASSERT_EQ(&temp, f1(temp)); ObFunction f2([](float &a) -> float* { return &a; }, test_allocator1); ASSERT_EQ(true, f2.is_valid());// alloc invalid, but not used ObFunction f3(test_allocator2); f3 = f1; ASSERT_EQ(true, f3.is_valid()); ObFunction f4(test_allocator1); f4 = std::move(f1); ASSERT_EQ(true, f4.is_valid()); } TEST_F(TestObFunction, standard_style) { ObFunction f = [](int) {}; if (f.is_valid()) {// 检查构造是否成功 f(0); } } TEST_F(TestObFunction, ob_style) { ObFunction f; if (OB_SUCCESS == f.assign([](int){})) {// 检查赋值是否成功 f(0); } } #define JUDGE_RECORDER(v1,v2,v3,v4,v5,v6,v7,v8,v9)\ ASSERT_EQ(recorder.function_default_construct_time, v1);\ ASSERT_EQ(recorder.function_copy_construct_time, v2);\ ASSERT_EQ(recorder.function_move_construct_time, v3);\ ASSERT_EQ(recorder.function_general_construct_time, v4);\ ASSERT_EQ(recorder.function_copy_equal_time, v5);\ ASSERT_EQ(recorder.function_move_equal_time, v6);\ ASSERT_EQ(recorder.function_general_equal_time, v7);\ ASSERT_EQ(recorder.derived_construct_time, v8);\ ASSERT_EQ(recorder.function_base_assign_time, v9);\ ASSERT_EQ(recorder.function_copy_assign_time, v2 + v5);\ ASSERT_EQ(recorder.function_move_assign_time, v3 + v6);\ ASSERT_EQ(recorder.function_general_assign_time, v4 + v7);\ recorder.reset(); TEST_F(TestObFunction, construct_path) { // implicit general construction ObFunction f1 = func1; JUDGE_RECORDER(0,0,0,1,0,0,0,1,0); // default construction and general assign ObFunction f2; f2 = func2; JUDGE_RECORDER(1,0,0,0,0,0,1,1,0); // just general assign f2 = Func5(); JUDGE_RECORDER(0,0,0,0,0,0,1,1,0); // copy assign f2 = f1; JUDGE_RECORDER(0,0,0,0,1,0,0,1,1); // move assign f2 = std::move(f1); JUDGE_RECORDER(0,0,0,0,0,1,0,1,1); // copy construction ObFunction f3 = f2; JUDGE_RECORDER(0,1,0,0,0,0,0,1,1); // move construction ObFunction f4 = std::move(f3); JUDGE_RECORDER(0,0,1,0,0,0,0,1,1); } struct BigObj { unsigned char val[41]; } ; struct SmallObj { unsigned char val[40]; }; // rvalue: small = big // big = small // small = small // big = big // lvalue: small = big // big = small // small = small // big = big TEST_F(TestObFunction, big_and_small_obj) { BigObj big; big.val[0] = 0; SmallObj small; ObFunction f1 = [big](){ return int(big.val[0]); };// big obj ObFunction f2 = [big](){ return int(big.val[0]); };// big obj ObFunction f3 = [big](){ return int(big.val[0]); };// big obj ObFunction f4 = [big](){ return int(big.val[0]); };// big obj ObFunction f5 = [big](){ return int(big.val[0]); };// big obj ObFunction f6 = [big](){ return int(big.val[0]); };// big obj ObFunction f7 = [big](){ return int(big.val[0]); };// big obj ObFunction f8 = [big](){ return int(big.val[0]); };// big obj ObFunction f9 = [small](){ return int(small.val[0]); };// small obj ObFunction f10 = [small](){ return int(small.val[0]); };// small obj ObFunction f11 = [small](){ return int(small.val[0]); };// small obj ObFunction f12 = [small](){ return int(small.val[0]); };// small obj ObFunction f13 = [small](){ return int(small.val[0]); };// small obj ObFunction f14 = [small](){ return int(small.val[0]); };// small obj ObFunction f15 = [small](){ return int(small.val[0]); };// small obj ObFunction f16 = [small](){ return int(small.val[0]); };// small obj recorder.reset(); // rvalue equal f9 = std::move(f1);// small = big JUDGE_RECORDER(0,0,0,0,0,1,0,1,1); f2 = std::move(f10);// big = small JUDGE_RECORDER(0,0,0,0,0,1,0,1,1); f3 = std::move(f4);// big = big JUDGE_RECORDER(0,0,0,0,0,1,0,0,0); f11 = std::move(f12);// small = small JUDGE_RECORDER(0,0,0,0,0,1,0,1,1); // lvalue equal f13 = f5;// small = big JUDGE_RECORDER(0,0,0,0,1,0,0,1,1); f6 = f14;// big = small JUDGE_RECORDER(0,0,0,0,1,0,0,1,1); f7 = f8;// big = big JUDGE_RECORDER(0,0,0,0,1,0,0,1,1); f15 = f16;// small = small JUDGE_RECORDER(0,0,0,0,1,0,0,1,1); // rvalue construct ObFunction f17 = std::move(f9);//construct big JUDGE_RECORDER(0,0,1,0,0,0,0,0,0); ObFunction f18 = std::move(f2);//construct small JUDGE_RECORDER(0,0,1,0,0,0,0,1,1); // lvalue construct ObFunction f19 = f17; JUDGE_RECORDER(0,1,0,0,0,0,0,1,1); ObFunction f20 = f18; JUDGE_RECORDER(0,1,0,0,0,0,0,1,1); } struct TestContructDestruct { TestContructDestruct(int64_t &count) : count_(count) { ++count_; } TestContructDestruct(const TestContructDestruct &rhs) : count_(rhs.count_) { ++count_; } ~TestContructDestruct() { --count_; } void operator()() {} int64_t &count_; }; TEST_F(TestObFunction, test_construct_destruct) { int64_t count = 0; TestContructDestruct test(count); ASSERT_EQ(count, 1); { ObFunction f = test; ASSERT_EQ(count, 2); } ASSERT_EQ(count, 1); } } } int main(int argc, char **argv) { system("rm -rf test_ob_function.log"); oceanbase::common::ObLogger &logger = oceanbase::common::ObLogger::get_logger(); logger.set_file_name("test_ob_function.log", false); logger.set_log_level(OB_LOG_LEVEL_DEBUG); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }