diff --git a/src/core_test/cases/cases_helper.h b/src/core_test/cases/cases_helper.h index d50ee78..d3b7520 100644 --- a/src/core_test/cases/cases_helper.h +++ b/src/core_test/cases/cases_helper.h @@ -31,8 +31,8 @@ protected: }; /// Forcibly modify private variables to reset state. -FORCE_ACCESS_VAR(AllCases, bool, available_) -FORCE_ACCESS_VAR(BasicRanges, bool, available_) +EXPOSE_VAR(AllCases, bool, available_) +EXPOSE_VAR(BasicRanges, bool, available_) /// Test fixture macro with custom test suite name. #define TEST_FF(test_suite_name, test_name) \ diff --git a/src/core_test/codec/short_code.cc b/src/core_test/codec/short_code.cc index fd2f1a6..845239f 100644 --- a/src/core_test/codec/short_code.cc +++ b/src/core_test/codec/short_code.cc @@ -21,8 +21,8 @@ using klotski::codec::SHORT_CODE_LIMIT; static const auto TEST_THREAD_NUM = 256; /// Forcibly modify private variables to reset state. -FORCE_ACCESS_VAR(AllCases, bool, available_) -FORCE_ACCESS_VAR(BasicRanges, bool, available_) +EXPOSE_VAR(AllCases, bool, available_) +EXPOSE_VAR(BasicRanges, bool, available_) /// Reset basic ranges build state, note it is thread-unsafe. void basic_ranges_reset() { diff --git a/src/core_test/ffi/all_cases.cc b/src/core_test/ffi/all_cases.cc index 30921b1..6b448b5 100644 --- a/src/core_test/ffi/all_cases.cc +++ b/src/core_test/ffi/all_cases.cc @@ -16,8 +16,8 @@ using klotski::cases::ALL_CASES_NUM; static constexpr std::string_view ALL_CASES_MD5 = "3888e9fab8d3cbb50908b12b147cfb23"; /// Forcibly modify private variables to reset state. -FORCE_ACCESS_VAR(AllCases, bool, available_) -FORCE_ACCESS_VAR(BasicRanges, bool, available_) +EXPOSE_VAR(AllCases, bool, available_) +EXPOSE_VAR(BasicRanges, bool, available_) /// Reset basic ranges build state, note it is thread-unsafe. void basic_ranges_reset() { diff --git a/src/core_test/utility/exposer.h b/src/core_test/utility/exposer.h index d41d1bc..2ae2082 100644 --- a/src/core_test/utility/exposer.h +++ b/src/core_test/utility/exposer.h @@ -1,13 +1,36 @@ #pragma once -/// The exposer can forcibly access private members of a class without changing -/// any code. It uses macros to construct a function that returns a reference -/// to the target member variable. +/// The Exposer can forcibly access private members of a class without changing +/// any code, and in most scenarios, it has zero over-cost. This exposer using +/// macros to construct function that return a reference of target variable or +/// calling target member function. + +/// It supports member variables, member functions and their const versions, as +/// well as static variables and functions. They are provided via the `EXPOSE_` +/// macro, A few examples are as follows. + +/// + EXPOSE_VAR(Demo, int, val) +/// + EXPOSE_CONST_VAR(Demo, int, cval) +/// +/// + EXPOSE_FUNC(Demo, int(const std::string &), func) +/// + EXPOSE_CONST_FUNC(Demo, int(const std::string &), cfunc) +/// +/// + EXPOSE_STATIC_VAR(Demo, int, sval) +/// + EXPOSE_STATIC_VAR(Demo, const int, scval) +/// +/// + EXPOSE_STATIC_FUNC(Demo, int(std::string_view), sfunc) + +/// The advantage of Exposer is that it can directly access the private members +/// without changing any content or compilation parameters. However, this is a +/// hack solution and should not be used in formal code, but only in test code. +/// Another simpler but less compatible way is to add the `-fno-access-control` +/// compilation parameter. #include // ----------------------------------------------------------------------------------------- // +namespace { namespace exposer { template @@ -16,6 +39,7 @@ struct Exposer { }; } // namespace exposer +} // namespace // ----------------------------------------------------------------------------------------- // @@ -25,27 +49,38 @@ struct Exposer { #define UNIQUE_TAG UNIQUE_TAG_IMPL(__COUNTER__) +#define NS_EXPOSER_START \ + namespace { \ + namespace exposer { \ + +#define NS_EXPOSER_END }} + // ----------------------------------------------------------------------------------------- // #define ACCESS_SVAR_IMPL(Class, Type, Member, Dummy) \ + NS_EXPOSER_START \ struct Dummy {}; \ template struct Exposer; \ constexpr Type* fetch(Dummy); \ constexpr Type& Class##_##Member() { \ return *fetch(Dummy{}); \ - } + } \ + NS_EXPOSER_END #define ACCESS_SFUNC_IMPL(Class, Proto, Member, Dummy, FuncTag) \ + NS_EXPOSER_START \ struct Dummy {}; \ using FuncTag = Proto; \ template struct Exposer; \ constexpr FuncTag* fetch(Dummy); \ - template \ + template \ constexpr decltype(auto) sfunc(Args &&...args) { \ return fetch(Dummy{})(std::forward(args)...); \ - } + } \ + NS_EXPOSER_END #define ACCESS_VAR_IMPL(Class, Type, ConstType, Member, Dummy) \ + NS_EXPOSER_START \ struct Dummy {}; \ template struct Exposer; \ constexpr Type Class::* fetch(Dummy); \ @@ -57,9 +92,11 @@ struct Exposer { } \ constexpr ConstType& Class##_##Member(const Class &c) { \ return c.*fetch(Dummy{}); \ - } + } \ + NS_EXPOSER_END #define ACCESS_FUNC_IMPL(Class, Proto, Member, Dummy, FuncTag) \ + NS_EXPOSER_START \ struct Dummy {}; \ using FuncTag = Proto; \ template struct Exposer; \ @@ -68,42 +105,31 @@ struct Exposer { requires std::is_same_v, Class> \ constexpr decltype(auto) Class##_##Member(C &&c, Args &&...args) { \ return (std::forward(c).*fetch(Dummy{}))(std::forward(args)...); \ - } + } \ + NS_EXPOSER_END // ----------------------------------------------------------------------------------------- // -#define EXPOSE_STATIC_VAR(Class, Type, Member) \ - namespace exposer { \ - ACCESS_SVAR_IMPL(Class, Type, Member, UNIQUE_TAG) \ - } +#define EXPOSE_STATIC_VAR(Class, Type, Member) \ + ACCESS_SVAR_IMPL(Class, Type, Member, UNIQUE_TAG) -#define EXPOSE_STATIC_FUNC(Class, Proto, Member) \ - namespace exposer { \ - ACCESS_SFUNC_IMPL(Class, Proto, Member, UNIQUE_TAG, UNIQUE_TAG) \ - } +#define EXPOSE_STATIC_FUNC(Class, Proto, Member) \ + ACCESS_SFUNC_IMPL(Class, Proto, Member, UNIQUE_TAG, UNIQUE_TAG) // ----------------------------------------------------------------------------------------- // -#define EXPOSE_VAR(Class, Type, Member) \ - namespace exposer { \ - ACCESS_VAR_IMPL(Class, Type, const Type, Member, UNIQUE_TAG) \ - } +#define EXPOSE_VAR(Class, Type, Member) \ + ACCESS_VAR_IMPL(Class, Type, const Type, Member, UNIQUE_TAG) -#define EXPOSE_FUNC(Class, Proto, Member) \ - namespace exposer { \ - ACCESS_FUNC_IMPL(Class, Proto, Member, UNIQUE_TAG, UNIQUE_TAG) \ - } +#define EXPOSE_FUNC(Class, Proto, Member) \ + ACCESS_FUNC_IMPL(Class, Proto, Member, UNIQUE_TAG, UNIQUE_TAG) // ----------------------------------------------------------------------------------------- // -#define EXPOSE_CONST_VAR(Class, Type, Member) \ - namespace exposer { \ - ACCESS_VAR_IMPL(Class, const Type, const Type, Member, UNIQUE_TAG) \ - } +#define EXPOSE_CONST_VAR(Class, Type, Member) \ + ACCESS_VAR_IMPL(Class, const Type, const Type, Member, UNIQUE_TAG) -#define EXPOSE_CONST_FUNC(Class, Proto, Member) \ - namespace exposer { \ - ACCESS_FUNC_IMPL(Class, Proto const, Member, UNIQUE_TAG, UNIQUE_TAG) \ - } +#define EXPOSE_CONST_FUNC(Class, Proto, Member) \ + ACCESS_FUNC_IMPL(Class, Proto const, Member, UNIQUE_TAG, UNIQUE_TAG) // ----------------------------------------------------------------------------------------- //