├── .github └── workflows │ └── main.yml ├── .gitignore ├── doc ├── accessor.md ├── getter.md ├── setter.md └── tutorial.md ├── include └── accessorpp │ ├── accessor.h │ ├── common.h │ ├── compiler.h │ ├── getter.h │ ├── internal │ ├── accessor_i.h │ └── typeutil_i.h │ └── setter.h ├── license ├── readme.md ├── tests ├── CMakeLists.txt ├── benchmark │ ├── CMakeLists.txt │ ├── b1_accessor.cpp │ ├── test.h │ └── testmain.cpp ├── build │ └── makefile ├── catch.hpp ├── tutorial │ ├── CMakeLists.txt │ ├── tutorial.h │ ├── tutorial_accessor.cpp │ ├── tutorial_view_model_binding.cpp │ └── tutorialmain.cpp └── unittest │ ├── CMakeLists.txt │ ├── test.h │ ├── test_accessor_basic.cpp │ ├── test_accessor_callback.cpp │ ├── test_accessor_default_no_getter_setter.cpp │ ├── test_accessor_object.cpp │ ├── test_accessor_operator_binary.cpp │ ├── test_accessor_operator_binary_assignment.cpp │ ├── test_accessor_operator_logic.cpp │ ├── test_accessor_operator_unary.cpp │ ├── test_getter.cpp │ ├── test_setter.cpp │ └── testmain.cpp └── tools └── generateops.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | Test: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [windows-latest, ubuntu-latest, macos-latest] 11 | build-type: [Release] 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Configure 16 | run: | 17 | mkdir -p build 18 | cmake -DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" -B build -S tests 19 | 20 | - name: Build Tests 21 | run: cmake --build build --config "${{ matrix.build-type }}" -j 22 | 23 | - name: Run unit tests 24 | run: cd build && ctest --build-config "${{ matrix.build-type }}" --progress --verbose -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/build/temp* 2 | tests/build/project* 3 | tests/coverage 4 | -------------------------------------------------------------------------------- /doc/accessor.md: -------------------------------------------------------------------------------- 1 | # Class Accessor reference 2 | 3 | ## Description 4 | 5 | Accessor is used to get and set value, it's a combination of Getter and Setter. 6 | Usually you should use Accessor instead of Getter or Setter. 7 | 8 | ## Header 9 | 10 | accessorpp/accessor.h 11 | 12 | ## Template parameters 13 | 14 | ```c++ 15 | template < 16 | typename Type, 17 | typename PoliciesType = DefaultPolicies 18 | > 19 | class Accessor : 20 | ``` 21 | `Type`: the underlying value type. 22 | `PoliciesType`: the policies. 23 | 24 | ## Policies 25 | 26 | accessorpp uses policy based design to configure and extend Accessor behavior. The last template parameter in Accessor is the policies class. Accessor has default policies class named `DefaultPolicies`. 27 | A policy is a type in the policies class. All policies must be public visible, so `struct` is commonly used to define the policies class. 28 | All policies are optional. If any policy is omitted, the default value is used. In fact `DefaultPolicies` is just an empty struct. 29 | 30 | ### Policy Storage 31 | 32 | The policy `Storage` determines how the underlying data is stored. It can have two kinds of types, 33 | `accessorpp::InternalStorage`: store the data in the Accessor. This is the default type. 34 | `accessorpp::ExternalStorage`: the Accessor doesn't hold the data, how the data is accessed depending on the getter and setter in the Accessor. 35 | InternalStorage and ExternalStorage defines different constructors and member functions in Accessor. You may treat them as different Accessor classes. 36 | 37 | Example code, 38 | ```c++ 39 | struct MyPolicies 40 | { 41 | using Storage = accessorpp::InternalStorage; 42 | }; 43 | accessorpp::Accessor accessor; 44 | ``` 45 | 46 | ```c++ 47 | struct MyPolicies 48 | { 49 | using Storage = accessorpp::ExternalStorage; 50 | }; 51 | int value = 0; 52 | accessorpp::Accessor accessor(&value, &value); 53 | ``` 54 | 55 | ### Policy OnChangingCallback and OnChangedCallback 56 | 57 | OnChangingCallback specifies the event handler type that's called before the underlying value is changed. OnChangedCallback specifies the event handler type that's called before the underlying value is changed. 58 | Ideally the `CallbackList` in [my eventpp library](https://github.com/wqking/eventpp) is perfect for such callback, otherwise `std::function` can be used. 59 | 60 | The callback can have three kinds of prototype, accessorpp will invoke the proper prototype automatically. 61 | ``` 62 | std::function; // or eventpp::CallbackList 63 | std::function; // or eventpp::CallbackList 64 | std::function; // or eventpp::CallbackList 65 | ``` 66 | 67 | The callback type must be default constructible. To add a handler to the accessor, call `void onChanging()` for OnChangingCallback and `void onChanged()` for OnChangedCallback. 68 | 69 | The default callback type is `void`, that doesn't trigger any events. 70 | 71 | Example code, 72 | ```c++ 73 | // Use std::function 74 | struct MyPolicies 75 | { 76 | using OnChangedCallback = std::function; 77 | }; 78 | accessorpp::Accessor accessor; 79 | accessor.onChanged() = [](const std::string & newValue) { 80 | std::cout << "New value is " << newValue << std::endl; 81 | }; 82 | accessor = "Hello"; 83 | ``` 84 | 85 | ```c++ 86 | // Use eventpp::CallbackList 87 | struct MyPolicies 88 | { 89 | using OnChangedCallback = eventpp::CallbackList; 90 | }; 91 | accessorpp::Accessor accessor; 92 | accessor.onChanged().append([](const std::string & newValue) { 93 | std::cout << "New value is " << newValue << std::endl; 94 | }); 95 | accessor = "Hello"; 96 | ``` 97 | 98 | ### Policy CallbackData 99 | 100 | If `CallbackData` is not `void`, we can call `Accessor::setWithCallbackData` to set the value with a callback data, and the callback data will be passed to OnChangingCallback and OnChangedCallback, if the callback accepts the data. 101 | Calling `Accessor::set` or assigning to an accessor will pass default constructed callback data. 102 | The `CallbackData` is useful to pass a "context" to the on change callback. For example, assume there is a text box on a GUI window. The text box listens to accessor's on change event to update the interface, while when the text box is changed such as the user types letters, the text box will also set to the accessor with the new text, which will also trigger the on change event. Without CallbackData, the text box needs update the interface two times when the user inputs, one is when the user typing, the other one is when the on change event is triggered by the text box. With CallbackData, when the text box sets the accessor, it can pass the CallbackData to indicate the setting is from itself, and in it's on change listener, it can check the CallbackData to avoid redundant updating. 103 | The tutorial "tutorial_view_model_binding.cpp" in the tests source code demonstrate the mechanism clearly. 104 | 105 | 106 | ## Constructors for InternalStorage 107 | 108 | #### Default constructor 109 | 110 | ```c++ 111 | Accessor(const Type & newValue = Type()); 112 | ``` 113 | 114 | Default constructor. 115 | `newValue` is the initial value. 116 | 117 | #### Construct from getter and setter 118 | ```c++ 119 | template 120 | Accessor(G && getter, S && setter, const ValueType & newValue = ValueType()); 121 | ``` 122 | 123 | The argument `getter` and `setter` are used to construct underlying `accessorpp::Getter` and `accessorpp::Setter`. 124 | `newValue` is the initial value. 125 | `getter` can be any type that's accepted by `accessor::Getter`. 126 | `setter` can be any type that's accepted by `accessor::Setter`. 127 | `getter` can be `accessor::DefaultGetter`, means the default getter is used. 128 | `setter` can be `accessor::DefaultSetter`, means the default setter is used. 129 | `setter` can be `accessor::NoSetter`, means there no setter used, so the accessor is read only. Setting to an accessor which setter is `NoSetter` will throw exception. 130 | 131 | It's possible that the getter and setter gets and sets external value, then the internal storage of the value is wasted. In such case, `accessorpp::ExternalStorage` should be used. 132 | 133 | Note: the getter and setter should call `accessor.directGet` and `accessor.directSet` to access the internal value in the accessor. 134 | 135 | Example code, 136 | ```c++ 137 | accessorpp::Accessor accessor( 138 | [&accessor]() { 139 | std::cout << "Getting value" << std::endl; 140 | return accessor.directGet(); 141 | }, 142 | [&accessor](const int value) { 143 | std::cout << "Setting value " << value << std::endl; 144 | accessor.directSet(value); 145 | } 146 | ); 147 | const int value = accessor; 148 | accessor = value + 1; 149 | ``` 150 | 151 | #### Construct from class members 152 | ```c++ 153 | template 154 | Accessor( 155 | G && getter, IG && getterInstance, 156 | S && setter, IS && setterInstance, 157 | const ValueType & newValue = ValueType() 158 | ); 159 | ``` 160 | 161 | `newValue` is the initial value. 162 | `getter` and `getterInstance` are used to construct underlying `accessorpp::Getter`. 163 | `setter` and `setterInstance` are used to construct underlying `accessorpp::Setter`. 164 | 165 | Example code, 166 | ```c++ 167 | struct MyClass 168 | { 169 | void setValue(const int newValue) { 170 | std::cout << "Setting value " << newValue << std::endl; 171 | value = newValue; 172 | } 173 | int value; 174 | }; 175 | MyClass instance { 5 }; 176 | accessorpp::Accessor accessor( 177 | &MyClass::value, &instance, 178 | &MyClass::setValue, &instance 179 | ); 180 | accessor = instance.value + 1; 181 | ``` 182 | 183 | Above constructor equals to, 184 | ```c++ 185 | accessorpp::Accessor accessor( 186 | accessorpp::Getter(&MyClass::value, &instance), 187 | accessorpp::Setter(&MyClass::setValue, &instance) 188 | ); 189 | ``` 190 | 191 | #### Copy constructor 192 | ```c++ 193 | Accessor(const Accessor & other); 194 | ``` 195 | 196 | Copy constructor. 197 | 198 | #### Move constructor 199 | ```c++ 200 | Accessor(Accessor && other); 201 | ``` 202 | 203 | Move constructor. 204 | 205 | ## Member functions for InternalStorage 206 | 207 | #### directGet, directGet 208 | ```c++ 209 | const Type & directGet() const; 210 | Type & directGet(); 211 | ``` 212 | 213 | Get the internal value directly. This is used to implement customized getter. 214 | 215 | ```c++ 216 | void directSet(const Type & newValue); 217 | ``` 218 | 219 | Set the internal value directly. This is used to implement customized setter. 220 | `directSet` doesn't respect read-only accessor, so it can set the value in a read-only accessor. 221 | `directSet` doesn't trigger any onChanging/onChanged events. 222 | 223 | ## Constructors and member functions for ExternalStorage 224 | 225 | ```c++ 226 | Accessor() noexcept; 227 | template 228 | Accessor(G && getter, S && setter); 229 | template 230 | Accessor( 231 | G && getter, IG && getterInstance, 232 | S && setter, IS && setterInstance 233 | ); 234 | Accessor(const Accessor & other); 235 | Accessor(Accessor && other); 236 | ``` 237 | 238 | The difference between ExternalStorage and InternalStorage is, constructors for ExternalStorage don't have the argument for initial value(`newValue`). ExternalStorage doesn't have functions `directGet` and `directSet`. 239 | 240 | ## Member functions for both InternalStorage and ExternalStorage 241 | 242 | Below functions are available in both InternalStorage and ExternalStorage. 243 | 244 | #### isReadOnly 245 | 246 | ```c++ 247 | constexpr bool isReadOnly() const; 248 | ``` 249 | 250 | Return true if the accessor is read-only. Setting to a read-only accessor will throw `std::logic_error`. 251 | 252 | #### get, operator ValueType 253 | ```c++ 254 | ValueType get(const void * instance = nullptr) const; 255 | operator ValueType() const; 256 | ``` 257 | 258 | Get the value. The function is same as `Getter::get`. 259 | 260 | #### set 261 | ```c++ 262 | Accessor & set(const ValueType & newValue, void * instance = nullptr); 263 | ``` 264 | 265 | Set the value. The function is same as `Setter::set`. 266 | 267 | #### setWithCallbackData 268 | ```c++ 269 | Accessor & setWithCallbackData(const ValueType & newValue, CD && callbackData, void * instance = nullptr); 270 | ``` 271 | 272 | Set the value with CallbackData. 273 | 274 | Input/output stream operator are overload. Accessor uses the underlying value with the stream. 275 | 276 | The `instance` argument in above get/set/setWithCallbackData functions are used to pass the object instance explicitly, if the underlying getter/setter is constructed with member data or member function and doesn't bind to an instance. In such case, the `instance` must be passed in explicitly, otherwise, the get/set/setWithCallbackData function will crash as if accessing an object of nullptr. 277 | 278 | ```c++ 279 | struct MyClass 280 | { 281 | void setValue(const int newValue) { 282 | value = newValue; 283 | } 284 | int value; 285 | }; 286 | MyClass instance; 287 | accessorpp::Accessor accessor(&MyClass::value, &MyClass::setValue); 288 | 289 | accessor.set(15, &instance); 290 | // Bang, crash. The instance is default nullptr 291 | //accessor = 15; 292 | 293 | // output 15 294 | std::cout << accessor.get(&instance) << std::endl; 295 | // Bang bang, crash. The instance is default nullptr 296 | // std::cout << (int)accessor << std::endl; 297 | 298 | accessor.set(16, &instance); 299 | // output 16 300 | std::cout << accessor.get(&instance) << std::endl; 301 | ``` 302 | 303 | #### I/O streaming 304 | ```c++ 305 | std::ostream & operator << (std::ostream & stream, const Accessor & accessor); 306 | std::istream & operator >> (std::istream & stream, Accessor & accessor); 307 | ``` 308 | 309 | ## Non-member free functions 310 | 311 | #### createAccessor 312 | 313 | ```c++ 314 | // #1 315 | template 316 | Accessor createAccessor(G && getter, S && setter, Policies = Policies()); 317 | 318 | // #2 319 | template < 320 | typename T, 321 | typename G, typename IG, typename S, typename IS, 322 | typename Policies = DefaultPolicies 323 | > 324 | Accessor createAccessor( 325 | G && getter, IG && getterInstance, 326 | S && setter, IS && setterInstance, 327 | Policies = Policies() 328 | ); 329 | 330 | // #3 331 | template 332 | auto createAccessor(G && getter, S && setter, Policies = Policies()) 333 | -> Accessor::Type, Policies>; 334 | 335 | // #4 336 | template < 337 | typename G, typename IG, typename S, typename IS, 338 | typename Policies = DefaultPolicies 339 | > 340 | auto createAccessor( 341 | G && getter, IG && getterInstance, 342 | S && setter, IS && setterInstance, 343 | Policies = Policies() 344 | ) 345 | -> Accessor::Type, Policies>; 346 | ``` 347 | 348 | Create accessor object using given arguments. 349 | #1 and #2 create the accessor using the explicitly passed-in type `T`. 350 | #3 and #4 deducts the accessor type automatically. 351 | 352 | #### createReadOnlyAccessor 353 | 354 | ```c++ 355 | // #1 356 | template 357 | Accessor createReadOnlyAccessor(G && getter, Policies = Policies()); 358 | 359 | // #2 360 | template 361 | Accessor createReadOnlyAccessor(G && getter, IG && getterInstance, Policies = Policies()); 362 | 363 | // #3 364 | template 365 | auto createReadOnlyAccessor(G && getter, Policies = Policies()) 366 | -> Accessor::Type, Policies>; 367 | 368 | // #4 369 | template 370 | auto createReadOnlyAccessor(G && getter, IG && getterInstance, Policies = Policies()) 371 | -> Accessor::Type, Policies>; 372 | ``` 373 | 374 | Create read-only accessor object using given arguments. 375 | #1 and #2 create the accessor using the explicitly passed-in type `T`. 376 | #3 and #4 deducts the accessor type automatically. 377 | -------------------------------------------------------------------------------- /doc/getter.md: -------------------------------------------------------------------------------- 1 | # Class Getter reference 2 | 3 | ## Description 4 | 5 | Getter is used to read value. 6 | Usually you should use Accessor instead of Getter. 7 | 8 | ## Header 9 | 10 | accessorpp/getter.h 11 | 12 | ## Template parameters 13 | 14 | ```c++ 15 | template 16 | class Getter; 17 | ``` 18 | `Type`: the underlying value type. 19 | 20 | ## Constructors 21 | 22 | #### Default constructor 23 | ```c++ 24 | Getter(); 25 | ``` 26 | 27 | Default constructor. Note: if the `Type` is a reference, the default constructor won't compile. 28 | 29 | #### Construct from data address 30 | ```c++ 31 | template 32 | explicit Getter(const U * address); 33 | ``` 34 | 35 | When reading from the getter, the getter gets value from `address` and cast the value to `Type`. 36 | Example code, 37 | ```c++ 38 | int value = 3; 39 | accessorpp::Getter getter(&value); 40 | // output 3 41 | std::cout << (int)getter << std::endl; 42 | value = 5; 43 | // output 5 44 | std::cout << (int)getter << std::endl; 45 | ``` 46 | 47 | #### Construct from class member address 48 | ```c++ 49 | template 50 | Getter(const U C::* address, const C * instance); 51 | ``` 52 | 53 | When reading from the getter, the getter gets value from member field `instance->*address` and cast the value to `Type`. 54 | Example code, 55 | ```c++ 56 | struct MyClass 57 | { 58 | int value; 59 | }; 60 | MyClass instance{ 8 }; 61 | accessorpp::Getter getter(&MyClass::value, &instance); 62 | // output 8 63 | std::cout << (int)getter << std::endl; 64 | instance.value = 9; 65 | // output 9 66 | std::cout << (int)getter << std::endl; 67 | ``` 68 | 69 | #### Construct from class member address, pass instance explicitly 70 | ```c++ 71 | template 72 | Getter(const U C::* address); 73 | ``` 74 | 75 | To read the value, call `Getter::get(instance)` with the object instance. 76 | Example code, 77 | ```c++ 78 | struct MyClass 79 | { 80 | int value; 81 | }; 82 | MyClass instance{ 8 }; 83 | accessorpp::Getter getter(&MyClass::value); 84 | // output 8, can't use (int)getter here 85 | std::cout << getter.get(&instance) << std::endl; 86 | instance.value = 9; 87 | // output 9 88 | std::cout << getter.get(&instance) << std::endl; 89 | ``` 90 | 91 | #### Construct from constant data 92 | ```c++ 93 | Getter(const Type & value); 94 | ``` 95 | 96 | When reading from the getter, the getter always returns 'value', that's to say, the getter returns constant 'value'. 97 | Example code, 98 | ```c++ 99 | int n = 9; 100 | // note n is passed by value 101 | accessorpp::Getter getter(n); 102 | // output 9 103 | std::cout << (int)getter << std::endl; 104 | n = 8; 105 | // still output 9 106 | std::cout << (int)getter << std::endl; 107 | ``` 108 | 109 | #### Construct from function 110 | ```c++ 111 | template 112 | explicit Getter(F func); 113 | ``` 114 | 115 | 'func' is a callable, such as function pointer, std::function, function object, or lambda. The prototype is `Type ()`. 116 | When reading from the getter, the getter returns `func()`. 117 | Example code, 118 | ```c++ 119 | int n = 9; 120 | accessorpp::Getter getter([&n]() -> int { return n++; }); 121 | // output 9 122 | std::cout << (int)getter << std::endl; 123 | // output 10 124 | std::cout << (int)getter << std::endl; 125 | ``` 126 | 127 | #### Construct from class member function 128 | ```c++ 129 | template 130 | explicit Getter(F func, C * instance); 131 | ``` 132 | 133 | `func` is a member function which prototype is `Type ()`, `instance` is the object pointer. 134 | When reading from the getter, the getter returns `instance->*func()`. 135 | Example code, 136 | ```c++ 137 | struct MyClass 138 | { 139 | int getValue() const { 140 | return value; 141 | } 142 | int value; 143 | }; 144 | MyClass instance{ 18 }; 145 | accessorpp::Getter getter(&MyClass::getValue, &instance); 146 | // output 18 147 | std::cout << (int)getter << std::endl; 148 | instance.value = 19; 149 | // output 19 150 | std::cout << (int)getter << std::endl; 151 | ``` 152 | 153 | #### Construct from class member function, pass instance explicitly 154 | ```c++ 155 | template 156 | explicit Getter(F func); 157 | ``` 158 | 159 | `func` is a member function which prototype is `Type ()`. 160 | To read the value, call `Getter::get(instance)` with the object instance. 161 | Example code, 162 | ```c++ 163 | struct MyClass 164 | { 165 | int getValue() const { 166 | return value; 167 | } 168 | int value; 169 | }; 170 | MyClass instance{ 18 }; 171 | accessorpp::Getter getter(&MyClass::getValue); 172 | // output 18 173 | std::cout << getter.get(&instance) << std::endl; 174 | instance.value = 19; 175 | // output 19 176 | std::cout << getter.get(&instance) << std::endl; 177 | ``` 178 | 179 | #### Copy constructor 180 | ```c++ 181 | Getter(const Getter & other); 182 | ``` 183 | 184 | Copy constructor. 185 | 186 | #### Move constructor 187 | ```c++ 188 | Getter(Getter && other); 189 | ``` 190 | 191 | Move constructor. 192 | 193 | ## Member functions 194 | 195 | #### get, operator Type 196 | 197 | ```c++ 198 | Type get(const void * instance = nullptr) const; 199 | operator Type() const; 200 | ``` 201 | 202 | Both returns the underlying value. 203 | If the getter is a class member and instance is not passed in constructor, the `instance` in the `get` function must be a valid object instance. Otherwise, `instance` can be nullptr. 204 | 205 | #### Output streaming 206 | 207 | ```c++ 208 | template 209 | std::ostream & operator << (std::ostream & stream, const Getter & getter); 210 | ``` 211 | 212 | Overloaded output stream operator. 213 | -------------------------------------------------------------------------------- /doc/setter.md: -------------------------------------------------------------------------------- 1 | # Class Setter reference 2 | 3 | ## Description 4 | 5 | Setter is used to write value. 6 | Usually you should use Accessor instead of Setter. 7 | 8 | ## Header 9 | 10 | accessorpp/setter.h 11 | 12 | ## Template parameters 13 | 14 | ```c++ 15 | template 16 | class Setter; 17 | ``` 18 | `Type`: the underlying value type. 19 | 20 | ## Constructors 21 | 22 | #### Default constructor 23 | ```c++ 24 | Setter(); 25 | ``` 26 | 27 | Default constructor. 28 | 29 | #### Construct from data address 30 | ```c++ 31 | template 32 | explicit Setter(U * address); 33 | ``` 34 | 35 | When setting to the setter, the setter sets value to `address`. 36 | Example code, 37 | ```c++ 38 | int value = 3; 39 | accessorpp::Setter setter(&value); 40 | setter = 5; 41 | // output 5 42 | std::cout << value << std::endl; 43 | setter = 8; 44 | // output 8 45 | std::cout << value << std::endl; 46 | ``` 47 | 48 | #### Construct from class member address 49 | ```c++ 50 | template 51 | Setter(const U C::* address, const C * instance); 52 | ``` 53 | 54 | When setting to the setter, the setter sets the value to member field `instance->*address`. 55 | Example code, 56 | ```c++ 57 | struct MyClass 58 | { 59 | int value; 60 | }; 61 | MyClass instance; 62 | accessorpp::Setter setter(&MyClass::value, &instance); 63 | setter = 18; 64 | // output 18 65 | std::cout << instance.value << std::endl; 66 | setter = 19; 67 | // output 19 68 | std::cout << instance.value << std::endl; 69 | ``` 70 | 71 | #### Construct from class member address, pass instance explicitly 72 | ```c++ 73 | template 74 | Setter(const U C::* address); 75 | ``` 76 | 77 | To set the value, call `Setter::set(value, instance)` with the object instance. 78 | Example code, 79 | ```c++ 80 | struct MyClass 81 | { 82 | int value; 83 | }; 84 | MyClass instance; 85 | accessorpp::Setter setter(&MyClass::value); 86 | // setter = 18; // can't assign directly, the setter needs an instance 87 | setter.set(18, &instance); 88 | // output 18 89 | std::cout << instance.value << std::endl; 90 | setter.set(19, &instance); 91 | // output 19 92 | std::cout << instance.value << std::endl; 93 | ``` 94 | 95 | #### Construct from function 96 | ```c++ 97 | template 98 | explicit Setter(F func); 99 | ``` 100 | 101 | 'func' is a callable, such as function pointer, std::function, function object, or lambda. The prototype is `void (Type)`. 102 | When setting to the setter, the setter invokes `func(value)`. 103 | Example code, 104 | ```c++ 105 | int n = 0; 106 | accessorpp::Setter setter([&n](const int value) { n = value; }); 107 | setter = 9; 108 | // output 9 109 | std::cout << n << std::endl; 110 | setter = 10; 111 | // output 10 112 | std::cout << n << std::endl; 113 | ``` 114 | 115 | #### Construct from class member function, pass instance explicitly 116 | ```c++ 117 | template 118 | explicit Setter(F func); 119 | ``` 120 | 121 | `func` is a member function which prototype is `void (Type)`. 122 | To set the value, call `Setter::set(value, instance)` with the object instance. 123 | Example code, 124 | ```c++ 125 | struct MyClass 126 | { 127 | void setValue(const int newValue) { 128 | value = newValue; 129 | } 130 | int value; 131 | }; 132 | MyClass instance; 133 | accessorpp::Setter setter(&MyClass::setValue); 134 | setter.set(15, &instance); 135 | // output 15 136 | std::cout << instance.value << std::endl; 137 | setter.set(16, &instance); 138 | // output 16 139 | std::cout << instance.value << std::endl; 140 | ``` 141 | 142 | #### Copy constructor 143 | ```c++ 144 | Setter(const Setter & other); 145 | ``` 146 | 147 | Copy constructor. 148 | 149 | #### Move constructor 150 | ```c++ 151 | Setter(Setter && other); 152 | ``` 153 | 154 | Move constructor. 155 | 156 | ## Member functions 157 | 158 | #### assignment, set 159 | 160 | ```c++ 161 | Setter & operator = (const ValueType & value, void * instance = nullptr); 162 | void set(const ValueType & value); 163 | ``` 164 | 165 | Both sets the underlying value. 166 | If the setter is a class member and instance is not passed in constructor, the `instance` in the `set` function must be a valid object instance. Otherwise, `instance` can be nullptr. 167 | 168 | #### Input streaming 169 | 170 | ```c++ 171 | template 172 | std::istream & operator >> (std::istream & stream, Setter & setter); 173 | ``` 174 | 175 | Overloaded input stream operator. 176 | -------------------------------------------------------------------------------- /doc/tutorial.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | Here is the tutorial code. There is compilable code in folder tests/tutorial/ 4 | The code is well commented. Please read the comment to see the tutorial. 5 | 6 | ```c++ 7 | // Include the header 8 | #include "accessorpp/accessor.h" 9 | 10 | #include "tutorial.h" 11 | 12 | #include 13 | 14 | TEST_CASE("Accessor tutorial 1, basic") 15 | { 16 | std::cout << std::endl << "Accessor tutorial 1, basic" << std::endl; 17 | 18 | // The namespace is accessor. 19 | // The first parameter is the type of the underlying value. 20 | // By default, the accessor stores the value in internal storage. 21 | // The argument 0 is the initial value, it can be omitted 22 | // and access will use the default value, i.e, 0 for int, "" for string, etc. 23 | accessorpp::Accessor accessor(0); 24 | 25 | // The argument can be omitted, and the code will look lik, 26 | // accessorpp::Accessor accessor; 27 | 28 | // To obtain the underlying value, we can cast the accessor to the type 29 | std::cout 30 | << "Accessor tutorial 1: the default value should be 0, got " 31 | << (int)accessor 32 | << std::endl; 33 | 34 | // Now let's set a new value to the accessor using the assignment operator = . 35 | accessor = 3; 36 | // We can also call Accessor::get() to obtain the underlying value 37 | std::cout 38 | << "Accessor tutorial 1: the new value should be 3, got " 39 | << accessor.get() 40 | << std::endl; 41 | 42 | // We can also call Accessor::set() to set the underlying value. 43 | accessor.set(8); 44 | // Accessor supports stream operator directly, so we don't need to 45 | // obtain the underlying value. 46 | std::cout 47 | << "Accessor tutorial 1: the new value should be 8, got " 48 | << accessor 49 | << std::endl; 50 | } 51 | 52 | TEST_CASE("Accessor tutorial 2, customized getter/setter") 53 | { 54 | std::cout << std::endl << "Accessor tutorial 2, customized getter/setter" << std::endl; 55 | 56 | { 57 | // Let's create an accessor with customized getter and setter. 58 | // The first argument is the getter, the second is the setter. 59 | // Note: the code requires C++20 standard in MSVC, otherwise 60 | // it gives error "lambda capture variable not found". 61 | // The code works with GCC and Clang. 62 | accessorpp::Accessor accessor( 63 | // This is the getter 64 | [&accessor]() { return accessor.directGet(); }, 65 | // This is the setter, it multiplies the incoming value with 2 66 | // and store to the accessor, for fun. 67 | [&accessor](const int value) { accessor.directSet(value * 2); } 68 | ); 69 | accessor = 3; 70 | std::cout 71 | << "Accessor tutorial 2: the accessor is set with 3, now the value should be 6, got " 72 | << (int)accessor 73 | << std::endl; 74 | } 75 | 76 | { 77 | // Now let's create an accessor with default getter and customized setter. 78 | // The first argument is accessorpp::defaultGetter, it uses the getter supplied by the accessor, 79 | // the second is the setter, the third argument is the inital value. 80 | // Likewise, there is accessorpp::defaultSetter for the default getter. 81 | // This time we use the syntax that MSVC is happy with. 82 | accessorpp::Accessor * ptr; 83 | accessorpp::Accessor accessor( 84 | accessorpp::defaultGetter, 85 | // Must capture ptr as reference, otherwise it won't get the value set later. 86 | [&ptr](const int value) { ptr->directSet(value + 1); }, 87 | 5 88 | ); 89 | ptr = &accessor; 90 | std::cout 91 | << "Accessor tutorial 2: the initial accessor is 5, got " 92 | << (int)accessor 93 | << std::endl; 94 | accessor = 8; 95 | std::cout 96 | << "Accessor tutorial 2: the accessor is set with 8, now the value should be 9, got " 97 | << accessor 98 | << std::endl; 99 | } 100 | 101 | { 102 | // Now let's create a read-only accessor with default getter. 103 | // The first argument is accessorpp::defaultGetter, it uses the getter supplied by the accessor, 104 | // the second is accessorpp::noSetter, it means no setter available, thus the accessor is read-only. 105 | accessorpp::Accessor accessor( 106 | accessorpp::defaultGetter, 107 | accessorpp::noSetter, 108 | 5 109 | ); 110 | std::cout << std::boolalpha; 111 | std::cout 112 | << "Accessor tutorial 2: the initial accessor is 5, got " 113 | << accessor 114 | << std::endl; 115 | std::cout 116 | << "Accessor tutorial 2: isReadOnly() should be true, got " 117 | << accessor.isReadOnly() 118 | << std::endl; 119 | try { 120 | // Assigning to read-only accessor will throw exception std::logic_error 121 | accessor = 8; 122 | } 123 | catch(const std::logic_error &) { 124 | std::cout 125 | << "Accessor tutorial 2: set to the read-only accessor causes std::logic_error, we got here." 126 | << std::endl; 127 | } 128 | } 129 | } 130 | 131 | TEST_CASE("Accessor tutorial 3, external storage") 132 | { 133 | std::cout << std::endl << "Accessor tutorial 3, external storage" << std::endl; 134 | 135 | // To use external storage, we need to define the policies which is a struct. 136 | struct MyPolicies 137 | { 138 | // The type Storage determines how the value is stored. 139 | // If Storage is accessorpp::InternalStorage, the Accessor stores the value inside the Accessor. 140 | // If Storage is accessorpp::ExternalStorage, the Accessor doesn't store the value, and 141 | // how the underlying value is accessed completely depends on the getter and setter. 142 | using Storage = accessorpp::ExternalStorage; 143 | }; 144 | 145 | int value = 38; 146 | // Pass MyPolicies as the second template argument, and pass &value as the getter and setter. 147 | // The access will read from 'value', and set to 'value'. 148 | accessorpp::Accessor accessor(&value, &value); 149 | std::cout 150 | << "Accessor tutorial 3: the initial value is 38, accessor = " 151 | << accessor 152 | << std::endl; 153 | accessor = 8; 154 | std::cout 155 | << "Accessor tutorial 3: the accessor is set with 8, now both value and accessor should be 8, got" 156 | << " value = " << value << " and accessor = " << (int)accessor 157 | << std::endl; 158 | } 159 | 160 | TEST_CASE("Accessor tutorial 4, on change event") 161 | { 162 | std::cout << std::endl << "Accessor tutorial 4, on change event" << std::endl; 163 | 164 | // To use onChanging and onChanged events, we need to define the policies which is a struct. 165 | struct MyPolicies 166 | { 167 | // Use std::function as OnChangingCallback, 168 | // and leave OnChangedCallback default which will not trigger onChanged event. 169 | // Here we use std::function for demostration purpose, 170 | // you should use the CallbackList class in eventpp library, 171 | // https://github.com/wqking/eventpp 172 | using OnChangingCallback = std::function; 173 | }; 174 | accessorpp::Accessor accessor; 175 | // Assign the onChanging event callback. If we use eventpp::CallbackList here, 176 | // we can add multiple callbacks to the event. 177 | accessor.onChanging() = [&accessor](const int newValue) { 178 | std::cout << "Accessor tutorial 4: the onChanging event got new value " << newValue 179 | << " and old value is " << accessor << std::endl; 180 | }; 181 | 182 | // In onChanging, the newValue will be 8, and old value is 0 183 | accessor = 8; 184 | // In onChanging, the newValue will be 38, and old value is 8 185 | accessor = 38; 186 | } 187 | 188 | ``` 189 | -------------------------------------------------------------------------------- /include/accessorpp/accessor.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_ACCESSOR_H_578722158669 15 | #define ACCESSORPP_ACCESSOR_H_578722158669 16 | 17 | #include "accessorpp/getter.h" 18 | #include "accessorpp/setter.h" 19 | #include "accessorpp/common.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace accessorpp { 28 | 29 | namespace private_ { 30 | 31 | struct DefaultGetter {}; 32 | struct DefaultSetter {}; 33 | struct NoSetter {}; 34 | 35 | } // namespace private_ 36 | 37 | constexpr private_::DefaultGetter defaultGetter; 38 | constexpr private_::DefaultSetter defaultSetter; 39 | constexpr private_::NoSetter noSetter; 40 | 41 | struct InternalStorage {}; 42 | struct ExternalStorage {}; 43 | 44 | #include "accessorpp/internal/accessor_i.h" 45 | 46 | template < 47 | typename Type, 48 | typename PoliciesType = DefaultPolicies 49 | > 50 | class Accessor : 51 | public private_::AccessorBase< 52 | Type, 53 | typename private_::SelectStorage::value, InternalStorage>::Type, 54 | PoliciesType 55 | >, 56 | public private_::OnChangingCallback< 57 | typename private_::SelectOnChangingCallback::value>::Type, 58 | typename private_::SelectCallbackData::value>::Type 59 | >, 60 | public private_::OnChangedCallback< 61 | typename private_::SelectOnChangedCallback::value>::Type, 62 | typename private_::SelectCallbackData::value>::Type 63 | > 64 | { 65 | private: 66 | using BaseType = private_::AccessorBase< 67 | Type, 68 | typename private_::SelectStorage::value, InternalStorage>::Type, 69 | PoliciesType 70 | >; 71 | using OnChangingCallbackType = private_::OnChangingCallback< 72 | typename private_::SelectOnChangingCallback::value>::Type, 73 | typename private_::SelectCallbackData::value>::Type 74 | >; 75 | using OnChangedCallbackType = private_::OnChangedCallback< 76 | typename private_::SelectOnChangedCallback::value>::Type, 77 | typename private_::SelectCallbackData::value>::Type 78 | >; 79 | 80 | public: 81 | using ValueType = Type; 82 | using GetterType = typename BaseType::GetterType; 83 | using SetterType = typename BaseType::SetterType; 84 | 85 | static constexpr bool internalStorage = std::is_same< 86 | typename private_::SelectStorage::value, InternalStorage>::Type, 87 | InternalStorage>::value; 88 | 89 | public: 90 | Accessor() noexcept 91 | : BaseType() 92 | { 93 | } 94 | 95 | // The explict static_cast is required, otherwise it will call 96 | // template explicit AccessorBase(P1 && p2) 97 | Accessor(const Accessor & other) 98 | : BaseType(static_cast(other)) { 99 | } 100 | 101 | using BaseType::BaseType; 102 | 103 | Accessor & operator = (const Accessor & other) { 104 | *this = other.get(); 105 | return *this; 106 | } 107 | 108 | Accessor & operator = (const ValueType & newValue) { 109 | return this->set(newValue); 110 | } 111 | 112 | Accessor & set(const ValueType & newValue, void * instance = nullptr) { 113 | this->doCheckWritable(); 114 | 115 | this->OnChangingCallbackType::invokeCallback(newValue); 116 | this->setter.set(newValue, instance); 117 | this->OnChangedCallbackType::invokeCallback(newValue); 118 | return *this; 119 | } 120 | 121 | template 122 | Accessor & setWithCallbackData(const ValueType & newValue, CD && callbackData, void * instance = nullptr) { 123 | this->doCheckWritable(); 124 | 125 | this->OnChangingCallbackType::invokeCallback(newValue, std::forward(callbackData)); 126 | this->setter.set(newValue, instance); 127 | this->OnChangedCallbackType::invokeCallback(newValue, std::forward(callbackData)); 128 | return *this; 129 | } 130 | 131 | ValueType get(const void * instance = nullptr) const { 132 | return this->getter.get(instance); 133 | } 134 | 135 | operator ValueType() const { 136 | return get(); 137 | } 138 | 139 | template 140 | void setGetter(const F & newGetter) { 141 | this->getter = GetterType(newGetter); 142 | } 143 | 144 | template 145 | void setSetter(const F & newSetter) { 146 | this->setter = SetterType(newSetter); 147 | } 148 | 149 | private: 150 | }; 151 | 152 | template 153 | struct IsAccessor : std::false_type 154 | { 155 | }; 156 | 157 | template < 158 | typename Type, 159 | typename PoliciesType 160 | > 161 | struct IsAccessor > : std::true_type 162 | { 163 | }; 164 | 165 | template 166 | struct AccessorValueType 167 | { 168 | using Type = typename std::decay::type; 169 | }; 170 | 171 | template < 172 | typename T, 173 | typename PoliciesType 174 | > 175 | struct AccessorValueType > 176 | { 177 | using Type = typename Accessor::ValueType; 178 | }; 179 | 180 | template 181 | Accessor createAccessor(G && getter, S && setter, Policies = Policies()) 182 | { 183 | return Accessor(std::forward(getter), std::forward(setter)); 184 | } 185 | 186 | template < 187 | typename T, 188 | typename G, typename IG, typename S, typename IS, 189 | typename Policies = DefaultPolicies 190 | > 191 | Accessor createAccessor( 192 | G && getter, IG && getterInstance, 193 | S && setter, IS && setterInstance, 194 | Policies = Policies() 195 | ) 196 | { 197 | return Accessor( 198 | std::forward(getter), std::forward(getterInstance), 199 | std::forward(setter), std::forward(setterInstance) 200 | ); 201 | } 202 | 203 | template 204 | auto createAccessor(G && getter, S && setter, Policies = Policies()) 205 | -> Accessor::Type, Policies> 206 | { 207 | using A = Accessor::Type, Policies>; 208 | return A( 209 | std::forward(getter), 210 | std::forward(setter) 211 | ); 212 | } 213 | 214 | template < 215 | typename G, typename IG, typename S, typename IS, 216 | typename Policies = DefaultPolicies 217 | > 218 | auto createAccessor( 219 | G && getter, IG && getterInstance, 220 | S && setter, IS && setterInstance, 221 | Policies = Policies() 222 | ) 223 | -> Accessor::Type, Policies> 224 | { 225 | using A = Accessor::Type, Policies>; 226 | return A( 227 | std::forward(getter), std::forward(getterInstance), 228 | std::forward(setter), std::forward(setterInstance) 229 | ); 230 | } 231 | 232 | template 233 | Accessor createReadOnlyAccessor(G && getter, Policies = Policies()) 234 | { 235 | using A = Accessor; 236 | return A(getter, noSetter); 237 | } 238 | 239 | template 240 | Accessor createReadOnlyAccessor(G && getter, IG && getterInstance, Policies) 241 | { 242 | using A = Accessor; 243 | return A(A::GetterType(std::forward(getter), std::forward(getterInstance)), noSetter); 244 | } 245 | 246 | template 247 | auto createReadOnlyAccessor(G && getter, Policies = Policies()) 248 | -> Accessor::Type, Policies> 249 | { 250 | using A = Accessor::Type, Policies>; 251 | return A(std::forward(getter), noSetter); 252 | } 253 | 254 | template 255 | auto createReadOnlyAccessor(G && getter, IG && getterInstance, Policies) 256 | -> Accessor::Type, Policies> 257 | { 258 | using A = Accessor::Type, Policies>; 259 | return A(A::GetterType(std::forward(getter), std::forward(getterInstance)), noSetter); 260 | } 261 | 262 | 263 | template 264 | auto operator << (std::ostream & stream, const T & accessor) 265 | -> typename std::enable_if::value, std::ostream &>::type 266 | { 267 | stream << accessor.get(); 268 | return stream; 269 | } 270 | 271 | template 272 | auto operator >> (std::istream & stream, T & accessor) 273 | -> typename std::enable_if::value, std::istream &>::type 274 | { 275 | typename T::ValueType value; 276 | stream >> value; 277 | accessor = value; 278 | return stream; 279 | } 280 | 281 | 282 | // Unary operators 283 | 284 | template 285 | auto operator ++ (T & a) 286 | -> typename std::enable_if::value, T &>::type 287 | { 288 | a += 1; 289 | return a; 290 | } 291 | 292 | template 293 | auto operator ++ (T & a, int) 294 | -> typename std::enable_if::value && T::internalStorage, T>::type 295 | { 296 | T result; 297 | result = a; 298 | ++a; 299 | return result; 300 | } 301 | 302 | template 303 | auto operator -- (T & a) 304 | -> typename std::enable_if::value, T &>::type 305 | { 306 | a -= 1; 307 | return a; 308 | } 309 | 310 | template 311 | auto operator -- (T & a, int) 312 | -> typename std::enable_if::value && T::internalStorage, T>::type 313 | { 314 | T result; 315 | result = a; 316 | --a; 317 | return result; 318 | } 319 | 320 | template 321 | auto operator ! (const T & a) 322 | -> typename std::enable_if::value && T::internalStorage, T>::type 323 | { 324 | T result; 325 | result = a; 326 | result = ! a.get(); 327 | return result; 328 | } 329 | 330 | template 331 | auto operator + (T & a) 332 | -> typename std::enable_if::value && T::internalStorage, T>::type 333 | { 334 | T result; 335 | result = a; 336 | result = +(typename AccessorValueType::Type)(a); 337 | return result; 338 | } 339 | 340 | template 341 | auto operator - (T & a) 342 | -> typename std::enable_if::value && T::internalStorage, T>::type 343 | { 344 | T result; 345 | result = a; 346 | result = -(typename AccessorValueType::Type)(a); 347 | return result; 348 | } 349 | 350 | 351 | 352 | 353 | // Below operators are generated from tool generateops.py. 354 | // Using C style cast because static_cast may fail on some case, such as char[] 355 | 356 | 357 | 358 | // Logic operators 359 | 360 | template 361 | auto operator == (const T & a, const U & b) 362 | -> typename std::enable_if::value, bool>::type 363 | { 364 | return (typename AccessorValueType::Type)(a) == (typename AccessorValueType::Type)(b); 365 | } 366 | 367 | template 368 | auto operator != (const T & a, const U & b) 369 | -> typename std::enable_if::value, bool>::type 370 | { 371 | return (typename AccessorValueType::Type)(a) != (typename AccessorValueType::Type)(b); 372 | } 373 | 374 | template 375 | auto operator > (const T & a, const U & b) 376 | -> typename std::enable_if::value, bool>::type 377 | { 378 | return (typename AccessorValueType::Type)(a) > (typename AccessorValueType::Type)(b); 379 | } 380 | 381 | template 382 | auto operator >= (const T & a, const U & b) 383 | -> typename std::enable_if::value, bool>::type 384 | { 385 | return (typename AccessorValueType::Type)(a) >= (typename AccessorValueType::Type)(b); 386 | } 387 | 388 | template 389 | auto operator < (const T & a, const U & b) 390 | -> typename std::enable_if::value, bool>::type 391 | { 392 | return (typename AccessorValueType::Type)(a) < (typename AccessorValueType::Type)(b); 393 | } 394 | 395 | template 396 | auto operator <= (const T & a, const U & b) 397 | -> typename std::enable_if::value, bool>::type 398 | { 399 | return (typename AccessorValueType::Type)(a) <= (typename AccessorValueType::Type)(b); 400 | } 401 | 402 | template 403 | auto operator && (const T & a, const U & b) 404 | -> typename std::enable_if::value, bool>::type 405 | { 406 | return (typename AccessorValueType::Type)(a) && (typename AccessorValueType::Type)(b); 407 | } 408 | 409 | template 410 | auto operator || (const T & a, const U & b) 411 | -> typename std::enable_if::value, bool>::type 412 | { 413 | return (typename AccessorValueType::Type)(a) || (typename AccessorValueType::Type)(b); 414 | } 415 | // Binary operators 416 | 417 | template 418 | auto operator + (const T & a, const U & b) 419 | -> typename std::enable_if::value && T::internalStorage, T>::type 420 | { 421 | T result(a); 422 | result = (typename AccessorValueType::Type)(a) + (typename AccessorValueType::Type)(b); 423 | return result; 424 | } 425 | 426 | template 427 | auto operator - (const T & a, const U & b) 428 | -> typename std::enable_if::value && T::internalStorage, T>::type 429 | { 430 | T result(a); 431 | result = (typename AccessorValueType::Type)(a) - (typename AccessorValueType::Type)(b); 432 | return result; 433 | } 434 | 435 | template 436 | auto operator * (const T & a, const U & b) 437 | -> typename std::enable_if::value && T::internalStorage, T>::type 438 | { 439 | T result(a); 440 | result = (typename AccessorValueType::Type)(a) * (typename AccessorValueType::Type)(b); 441 | return result; 442 | } 443 | 444 | template 445 | auto operator / (const T & a, const U & b) 446 | -> typename std::enable_if::value && T::internalStorage, T>::type 447 | { 448 | T result(a); 449 | result = (typename AccessorValueType::Type)(a) / (typename AccessorValueType::Type)(b); 450 | return result; 451 | } 452 | 453 | template 454 | auto operator % (const T & a, const U & b) 455 | -> typename std::enable_if::value && T::internalStorage, T>::type 456 | { 457 | T result(a); 458 | result = (typename AccessorValueType::Type)(a) % (typename AccessorValueType::Type)(b); 459 | return result; 460 | } 461 | 462 | template 463 | auto operator & (const T & a, const U & b) 464 | -> typename std::enable_if::value && T::internalStorage, T>::type 465 | { 466 | T result(a); 467 | result = (typename AccessorValueType::Type)(a) & (typename AccessorValueType::Type)(b); 468 | return result; 469 | } 470 | 471 | template 472 | auto operator | (const T & a, const U & b) 473 | -> typename std::enable_if::value && T::internalStorage, T>::type 474 | { 475 | T result(a); 476 | result = (typename AccessorValueType::Type)(a) | (typename AccessorValueType::Type)(b); 477 | return result; 478 | } 479 | 480 | template 481 | auto operator ^ (const T & a, const U & b) 482 | -> typename std::enable_if::value && T::internalStorage, T>::type 483 | { 484 | T result(a); 485 | result = (typename AccessorValueType::Type)(a) ^ (typename AccessorValueType::Type)(b); 486 | return result; 487 | } 488 | 489 | template 490 | auto operator << (const T & a, const U & b) 491 | -> typename std::enable_if::value && T::internalStorage, T>::type 492 | { 493 | T result(a); 494 | result = (typename AccessorValueType::Type)(a) << (typename AccessorValueType::Type)(b); 495 | return result; 496 | } 497 | 498 | template 499 | auto operator >> (const T & a, const U & b) 500 | -> typename std::enable_if::value && T::internalStorage, T>::type 501 | { 502 | T result(a); 503 | result = (typename AccessorValueType::Type)(a) >> (typename AccessorValueType::Type)(b); 504 | return result; 505 | } 506 | // Binary assignment operators 507 | 508 | template 509 | auto operator += (T & a, const U & b) 510 | -> typename std::enable_if::value, T &>::type 511 | { 512 | a = (typename AccessorValueType::Type)(a) + (typename AccessorValueType::Type)(b); 513 | return a; 514 | } 515 | 516 | template 517 | auto operator -= (T & a, const U & b) 518 | -> typename std::enable_if::value, T &>::type 519 | { 520 | a = (typename AccessorValueType::Type)(a) - (typename AccessorValueType::Type)(b); 521 | return a; 522 | } 523 | 524 | template 525 | auto operator *= (T & a, const U & b) 526 | -> typename std::enable_if::value, T &>::type 527 | { 528 | a = (typename AccessorValueType::Type)(a) * (typename AccessorValueType::Type)(b); 529 | return a; 530 | } 531 | 532 | template 533 | auto operator /= (T & a, const U & b) 534 | -> typename std::enable_if::value, T &>::type 535 | { 536 | a = (typename AccessorValueType::Type)(a) / (typename AccessorValueType::Type)(b); 537 | return a; 538 | } 539 | 540 | template 541 | auto operator %= (T & a, const U & b) 542 | -> typename std::enable_if::value, T &>::type 543 | { 544 | a = (typename AccessorValueType::Type)(a) % (typename AccessorValueType::Type)(b); 545 | return a; 546 | } 547 | 548 | template 549 | auto operator &= (T & a, const U & b) 550 | -> typename std::enable_if::value, T &>::type 551 | { 552 | a = (typename AccessorValueType::Type)(a) & (typename AccessorValueType::Type)(b); 553 | return a; 554 | } 555 | 556 | template 557 | auto operator |= (T & a, const U & b) 558 | -> typename std::enable_if::value, T &>::type 559 | { 560 | a = (typename AccessorValueType::Type)(a) | (typename AccessorValueType::Type)(b); 561 | return a; 562 | } 563 | 564 | template 565 | auto operator ^= (T & a, const U & b) 566 | -> typename std::enable_if::value, T &>::type 567 | { 568 | a = (typename AccessorValueType::Type)(a) ^ (typename AccessorValueType::Type)(b); 569 | return a; 570 | } 571 | 572 | template 573 | auto operator <<= (T & a, const U & b) 574 | -> typename std::enable_if::value, T &>::type 575 | { 576 | a = (typename AccessorValueType::Type)(a) << (typename AccessorValueType::Type)(b); 577 | return a; 578 | } 579 | 580 | template 581 | auto operator >>= (T & a, const U & b) 582 | -> typename std::enable_if::value, T &>::type 583 | { 584 | a = (typename AccessorValueType::Type)(a) >> (typename AccessorValueType::Type)(b); 585 | return a; 586 | } 587 | 588 | 589 | } // namespace accessorpp 590 | 591 | #endif 592 | -------------------------------------------------------------------------------- /include/accessorpp/common.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_COMMON_H_578722158669 15 | #define ACCESSORPP_COMMON_H_578722158669 16 | 17 | namespace accessorpp { 18 | 19 | struct DefaultPolicies {}; 20 | 21 | 22 | 23 | } // namespace accessorpp 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/accessorpp/compiler.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_COMPILER_H_873589173319 15 | #define ACCESSORPP_COMPILER_H_873589173319 16 | 17 | #if defined(_MSC_VER) 18 | #define ACCESSORPP_COMPILER_VC 19 | #endif 20 | // When using clang in Visual Studio, both _MSC_VER and __clang__ can be defined 21 | #if defined(__clang__) 22 | #define ACCESSORPP_COMPILER_CLANG 23 | #elif defined(__GNUC__) 24 | #define ACCESSORPP_COMPILER_GCC 25 | #else 26 | #define ACCESSORPP_COMPILER_UNKNOWN 27 | #endif 28 | 29 | #if __cplusplus >= 201703L 30 | #define ACCESSORPP_SUPPORT_STANDARD_17 31 | #else 32 | // Visual Studio 2017 RTW (15.0) 33 | #if defined(_MSC_VER) && _MSC_VER >= 1910 34 | // the code is inspired by vcruntime.h 35 | #if defined(_MSVC_LANG) && (_MSVC_LANG > 201402L) 36 | #define ACCESSORPP_SUPPORT_STANDARD_17 37 | #endif 38 | #endif 39 | #endif 40 | 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /include/accessorpp/getter.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_GETTER_H_578722158669 15 | #define ACCESSORPP_GETTER_H_578722158669 16 | 17 | #include "internal/typeutil_i.h" 18 | #include "accessorpp/common.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace accessorpp { 25 | 26 | namespace private_ { 27 | 28 | class DefaultClassTypeSetter 29 | { 30 | protected: 31 | template 32 | void setClassType() { 33 | } 34 | }; 35 | 36 | } // namespace private_ 37 | 38 | template 39 | class Getter : 40 | public private_::SelectClassTypeSetter::value, private_::DefaultClassTypeSetter>::Type 41 | { 42 | public: 43 | using Type = Type_; 44 | using ValueType = typename private_::GetUnderlyingType::Type; 45 | 46 | public: 47 | Getter() 48 | : getterFunc() 49 | { 50 | } 51 | 52 | template 53 | explicit Getter(const U * address, 54 | typename std::enable_if::value>::type * = nullptr) 55 | : getterFunc([address](const void *)->Type { return (Type)*address; }) 56 | { 57 | } 58 | 59 | template 60 | Getter(const U C::* address, const C * instance, 61 | typename std::enable_if::value>::type * = nullptr) 62 | : getterFunc([address, instance](const void *)->Type { return (Type)(instance->*address); }) 63 | { 64 | this->template setClassType(); 65 | } 66 | 67 | template 68 | Getter(const U C::* address, 69 | typename std::enable_if::value>::type * = nullptr) 70 | : getterFunc([address](const void * instance)->Type { return (Type)(static_cast(instance)->*address); }) 71 | { 72 | this->template setClassType(); 73 | } 74 | 75 | explicit Getter(const Type & value) 76 | : getterFunc([value](const void *)->Type { return value; }) 77 | { 78 | } 79 | 80 | template 81 | explicit Getter(F func, 82 | typename std::enable_if::value>::type * = nullptr) 83 | : getterFunc([func](const void *)->Type { return (Type)func(); }) 84 | { 85 | } 86 | 87 | template 88 | Getter(F func, C * instance, 89 | typename std::enable_if< 90 | private_::CallableTypeChecker::isClassMember 91 | && std::is_convertible::ResultType, ValueType>::value 92 | >::type * = nullptr) 93 | : getterFunc([func, instance](const void *)->Type { return (Type)((instance->*func)()); }) 94 | { 95 | this->template setClassType::ClassType>(); 96 | } 97 | 98 | template 99 | Getter(F func, 100 | typename std::enable_if< 101 | private_::CallableTypeChecker::isClassMember 102 | && std::is_convertible::ResultType, ValueType>::value 103 | >::type * = nullptr) 104 | : getterFunc([func](const void * instance)->Type { 105 | return (Type)((static_cast::ClassType *>(instance)->*func)()); 106 | }) 107 | { 108 | this->template setClassType::ClassType>(); 109 | } 110 | 111 | Getter(const Getter & other) 112 | : getterFunc(other.getterFunc) 113 | { 114 | } 115 | 116 | Getter(Getter && other) 117 | : getterFunc(std::move(other.getterFunc)) 118 | { 119 | } 120 | 121 | Getter & operator = (const Getter & other) { 122 | getterFunc = other.getterFunc; 123 | return *this; 124 | } 125 | 126 | Getter & operator = (Getter && other) { 127 | getterFunc = std::move(other.getterFunc); 128 | return *this; 129 | } 130 | 131 | Type get(const void * instance = nullptr) const { 132 | return getterFunc(instance); 133 | } 134 | 135 | operator Type() const { 136 | return get(); 137 | } 138 | 139 | private: 140 | std::function getterFunc; 141 | }; 142 | 143 | template 144 | struct IsGetter : std::false_type 145 | { 146 | }; 147 | 148 | template 149 | struct IsGetter > : std::true_type 150 | { 151 | }; 152 | 153 | template 154 | std::ostream & operator << (std::ostream & stream, const Getter & getter) 155 | { 156 | stream << getter.get(); 157 | return stream; 158 | } 159 | 160 | 161 | } // namespace accessorpp 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /include/accessorpp/internal/accessor_i.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_ACCESSOR_I_H_578722158669 15 | #define ACCESSORPP_ACCESSOR_I_H_578722158669 16 | 17 | namespace private_ { 18 | 19 | template 20 | struct ChangeCallbackBase 21 | { 22 | CallbackType & getCallback() { 23 | return callback; 24 | } 25 | 26 | const CallbackType & getCallback() const { 27 | return callback; 28 | } 29 | 30 | CallbackType callback; 31 | }; 32 | 33 | template 34 | class ChangeCallback : protected ChangeCallbackBase 35 | { 36 | protected: 37 | template 38 | void invokeCallback( 39 | const ValueType & newValue 40 | ) { 41 | doInvokeCallback(newValue, CallbackDataType()); 42 | } 43 | 44 | template 45 | void invokeCallback( 46 | const ValueType & newValue, 47 | const CallbackDataType & data 48 | ) { 49 | doInvokeCallback(newValue, data); 50 | } 51 | 52 | private: 53 | template 54 | auto doInvokeCallback( 55 | const ValueType & /*newValue*/, 56 | const CallbackDataType & /*data*/ 57 | ) 58 | -> typename std::enable_if::value, void>::type 59 | { 60 | this->callback(); 61 | } 62 | 63 | template 64 | auto doInvokeCallback( 65 | const ValueType & newValue, 66 | const CallbackDataType & /*data*/ 67 | ) 68 | -> typename std::enable_if::value, void>::type 69 | { 70 | this->callback(newValue); 71 | } 72 | 73 | template 74 | auto doInvokeCallback( 75 | const ValueType & newValue, 76 | const CallbackDataType & data 77 | ) 78 | -> typename std::enable_if::value, void>::type 79 | { 80 | this->callback(newValue, data); 81 | } 82 | }; 83 | 84 | template 85 | class ChangeCallback : protected ChangeCallbackBase 86 | { 87 | protected: 88 | template 89 | void invokeCallback( 90 | const ValueType & newValue 91 | ) { 92 | doInvokeCallback(newValue); 93 | } 94 | 95 | template 96 | void invokeCallback( 97 | const ValueType & newValue, 98 | Data && 99 | ) { 100 | doInvokeCallback(newValue); 101 | } 102 | 103 | private: 104 | template 105 | auto doInvokeCallback( 106 | const ValueType & /*newValue*/ 107 | ) 108 | -> typename std::enable_if::value, void>::type 109 | { 110 | this->callback(); 111 | } 112 | 113 | template 114 | auto doInvokeCallback( 115 | const ValueType & newValue 116 | ) 117 | -> typename std::enable_if::value, void>::type 118 | { 119 | this->callback(newValue); 120 | } 121 | }; 122 | 123 | class DummyChangeCallback 124 | { 125 | protected: 126 | template 127 | void invokeCallback(const ValueType & /*newValue*/) 128 | { 129 | } 130 | 131 | template 132 | void invokeCallback(const ValueType & /*newValue*/, Data &&) 133 | { 134 | } 135 | 136 | }; 137 | 138 | template 139 | class OnChangingCallback : public ChangeCallback 140 | { 141 | private: 142 | using super = ChangeCallback ; 143 | 144 | public: 145 | using super::super; 146 | 147 | const CallbackType & onChanging() const { 148 | return super::getCallback(); 149 | } 150 | 151 | CallbackType & onChanging() { 152 | return super::getCallback(); 153 | } 154 | }; 155 | 156 | template 157 | class OnChangingCallback : public DummyChangeCallback 158 | { 159 | }; 160 | 161 | template 162 | class OnChangedCallback : public ChangeCallback 163 | { 164 | private: 165 | using super = ChangeCallback ; 166 | 167 | public: 168 | using super::super; 169 | 170 | const CallbackType & onChanged() const { 171 | return super::getCallback(); 172 | } 173 | 174 | CallbackType & onChanged() { 175 | return super::getCallback(); 176 | } 177 | }; 178 | 179 | template 180 | class OnChangedCallback : public DummyChangeCallback 181 | { 182 | }; 183 | 184 | template 185 | class AccessorRoot 186 | { 187 | protected: 188 | using GetterType = Getter; 189 | using SetterType = Setter; 190 | 191 | public: 192 | AccessorRoot() noexcept 193 | : 194 | getter(), 195 | setter(), 196 | readOnly(false) 197 | { 198 | } 199 | 200 | AccessorRoot(const AccessorRoot & other) 201 | : 202 | getter(other.getter), 203 | setter(other.setter), 204 | readOnly(other.readOnly) 205 | { 206 | } 207 | 208 | AccessorRoot(AccessorRoot && other) 209 | : 210 | getter(std::move(other.getter)), 211 | setter(std::move(other.setter)), 212 | readOnly(std::move(other.readOnly)) 213 | { 214 | } 215 | 216 | template 217 | AccessorRoot(G && getter, S && setter) 218 | : 219 | getter(std::forward(getter)), 220 | setter(std::forward(setter)), 221 | readOnly(false) 222 | { 223 | } 224 | 225 | template 226 | AccessorRoot(G && getter, NoSetter) 227 | : 228 | getter(std::forward(getter)), 229 | setter(), 230 | readOnly(true) 231 | { 232 | } 233 | 234 | template 235 | AccessorRoot( 236 | G && getter, IG && getterInstance, 237 | S && setter, IS && setterInstance 238 | ) 239 | : 240 | getter(std::forward(getter), std::forward(getterInstance)), 241 | setter(std::forward(setter), std::forward(setterInstance)), 242 | readOnly(false) 243 | { 244 | } 245 | 246 | constexpr bool isReadOnly() const { 247 | return readOnly; 248 | } 249 | 250 | const GetterType & getGetter() const { 251 | return getter; 252 | } 253 | 254 | SetterType & getSetter() const { 255 | return setter; 256 | } 257 | 258 | protected: 259 | void doCheckWritable() const { 260 | if(readOnly) { 261 | throw std::logic_error("Can't set to read-only accessor."); 262 | } 263 | } 264 | 265 | protected: 266 | GetterType getter; 267 | SetterType setter; 268 | const bool readOnly; 269 | }; 270 | 271 | template 272 | class AccessorBase; 273 | 274 | template 275 | class AccessorBase : public AccessorRoot 276 | { 277 | private: 278 | using super = AccessorRoot; 279 | using ValueType = typename std::remove_cv::type>::type; 280 | 281 | public: 282 | using GetterType = typename super::GetterType; 283 | using SetterType = typename super::SetterType; 284 | 285 | public: 286 | AccessorBase(const ValueType & newValue = ValueType()) 287 | : 288 | super(&AccessorBase::value, this, &AccessorBase::value, this), 289 | value(newValue) 290 | { 291 | } 292 | 293 | AccessorBase(const AccessorBase & other) 294 | : 295 | super(&AccessorBase::value, this, &AccessorBase::value, this), 296 | value(other.value) 297 | { 298 | } 299 | 300 | AccessorBase(AccessorBase && other) 301 | : 302 | super(static_cast(other)), 303 | value(std::move(other.value)) 304 | { 305 | } 306 | 307 | template 308 | AccessorBase(G && getter, S && setter, const ValueType & newValue = ValueType()) 309 | : 310 | super(std::forward(getter), 311 | std::forward(setter)), 312 | value(newValue) 313 | { 314 | } 315 | 316 | template 317 | AccessorBase(DefaultGetter, S && setter, const ValueType & newValue = ValueType()) 318 | : 319 | super(GetterType(&AccessorBase::value, this), 320 | std::forward(setter)), 321 | value(newValue) 322 | { 323 | } 324 | 325 | template 326 | AccessorBase(G && getter, DefaultSetter, const ValueType & newValue = ValueType()) 327 | : 328 | super(GetterType(std::forward(getter)), 329 | SetterType(&AccessorBase::value, this)), 330 | value(newValue) 331 | { 332 | } 333 | 334 | AccessorBase(DefaultGetter, DefaultSetter, const ValueType & newValue = ValueType()) 335 | : 336 | super(GetterType(&AccessorBase::value, this), 337 | SetterType(&AccessorBase::value, this)), 338 | value(newValue) 339 | { 340 | } 341 | 342 | template 343 | AccessorBase( 344 | G && getter, IG && getterInstance, 345 | S && setter, IS && setterInstance, 346 | const ValueType & newValue = ValueType() 347 | ) 348 | : 349 | super(std::forward(getter), std::forward(getterInstance), 350 | std::forward(setter), std::forward(setterInstance)), 351 | value(newValue) 352 | { 353 | } 354 | 355 | // The functions directGet and directSet should be used to implement getter/setter, 356 | // They don't respect "readOnly", and don't trigger any events. 357 | const ValueType & directGet() const { 358 | return value; 359 | } 360 | 361 | ValueType & directGet() { 362 | return value; 363 | } 364 | 365 | // This doesn't respect "readOnly". 366 | void directSet(const ValueType & newValue) { 367 | value = newValue; 368 | } 369 | 370 | private: 371 | ValueType value; 372 | }; 373 | 374 | template 375 | class AccessorBase : public AccessorRoot 376 | { 377 | private: 378 | using super = AccessorRoot; 379 | 380 | public: 381 | using GetterType = typename super::GetterType; 382 | using SetterType = typename super::SetterType; 383 | 384 | public: 385 | using super::super; 386 | }; 387 | 388 | 389 | } // namespace private_ 390 | 391 | 392 | 393 | #endif 394 | -------------------------------------------------------------------------------- /include/accessorpp/internal/typeutil_i.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_TYPEUTIL_I_H_582750282985 15 | #define ACCESSORPP_TYPEUTIL_I_H_582750282985 16 | 17 | #include "accessorpp/compiler.h" 18 | 19 | #include 20 | #include 21 | 22 | namespace accessorpp { 23 | 24 | namespace private_ { 25 | 26 | template 27 | struct CanInvoke 28 | { 29 | template 30 | static auto invoke(int) -> decltype(std::declval()(std::declval()...), std::true_type()); 31 | 32 | template 33 | static auto invoke(...)->std::false_type; 34 | 35 | enum { 36 | value = !! decltype(invoke(0))() 37 | }; 38 | }; 39 | 40 | template 41 | struct HelperCallableTypeChecker 42 | { 43 | static constexpr bool isFunction = false; 44 | static constexpr bool isClassMember = false; 45 | using ClassType = void; 46 | using ResultType = void; 47 | }; 48 | 49 | template 50 | struct HelperCallableTypeChecker 51 | { 52 | static constexpr bool isFunction = true; 53 | static constexpr bool isClassMember = false; 54 | using ClassType = void; 55 | using ResultType = RT; 56 | }; 57 | 58 | template 59 | struct HelperCallableTypeChecker : HelperCallableTypeChecker 60 | { 61 | }; 62 | 63 | template 64 | struct HelperCallableTypeChecker 65 | { 66 | static constexpr bool isFunction = true; 67 | static constexpr bool isClassMember = true; 68 | using ClassType = C; 69 | using ResultType = RT; 70 | }; 71 | 72 | template 73 | struct HelperCallableTypeChecker : HelperCallableTypeChecker {}; 74 | template 75 | struct HelperCallableTypeChecker : HelperCallableTypeChecker {}; 76 | template 77 | struct HelperCallableTypeChecker : HelperCallableTypeChecker {}; 78 | 79 | #ifdef ACCESSORPP_SUPPORT_STANDARD_17 80 | template 81 | struct HelperCallableTypeChecker : HelperCallableTypeChecker {}; 82 | template 83 | struct HelperCallableTypeChecker : HelperCallableTypeChecker {}; 84 | template 85 | struct HelperCallableTypeChecker : HelperCallableTypeChecker {}; 86 | #endif 87 | 88 | template 89 | struct CallableTypeChecker : HelperCallableTypeChecker 90 | { 91 | }; 92 | 93 | template 94 | struct DetectValueType; 95 | 96 | template 97 | struct DetectValueType 98 | { 99 | using Type = T; 100 | }; 101 | template struct DetectValueType : DetectValueType {}; 102 | template struct DetectValueType : DetectValueType {}; 103 | template struct DetectValueType : DetectValueType {}; 104 | 105 | template 106 | struct DetectValueType 107 | { 108 | using Type = T; 109 | }; 110 | 111 | template 112 | struct DetectValueType 113 | { 114 | using Type = T; 115 | }; 116 | 117 | template 118 | struct DetectValueType 119 | { 120 | using Type = T; 121 | }; 122 | template struct DetectValueType : DetectValueType {}; 123 | template struct DetectValueType : DetectValueType {}; 124 | template struct DetectValueType : DetectValueType {}; 125 | 126 | template 127 | struct DetectValueType 128 | { 129 | using Type = T; 130 | }; 131 | template 132 | struct DetectValueType : DetectValueType {}; 133 | template 134 | struct DetectValueType : DetectValueType {}; 135 | template 136 | struct DetectValueType : DetectValueType {}; 137 | 138 | #ifdef ACCESSORPP_SUPPORT_STANDARD_17 139 | template 140 | struct DetectValueType : DetectValueType {}; 141 | template 142 | struct DetectValueType : DetectValueType {}; 143 | template 144 | struct DetectValueType : DetectValueType {}; 145 | #endif 146 | 147 | template 148 | struct GetUnderlyingType 149 | { 150 | using Type = typename std::remove_cv::type>::type; 151 | }; 152 | 153 | template 154 | struct HasTypeOnChangingCallback 155 | { 156 | template static std::true_type test(typename C::OnChangingCallback *) ; 157 | template static std::false_type test(...); 158 | 159 | enum { value = !! decltype(test(0))() }; 160 | }; 161 | template struct SelectOnChangingCallback { using Type = typename T::OnChangingCallback; }; 162 | template struct SelectOnChangingCallback { using Type = void; }; 163 | 164 | template 165 | struct HasTypeOnChangedCallback 166 | { 167 | template static std::true_type test(typename C::OnChangedCallback *) ; 168 | template static std::false_type test(...); 169 | 170 | enum { value = !! decltype(test(0))() }; 171 | }; 172 | template struct SelectOnChangedCallback { using Type = typename T::OnChangedCallback; }; 173 | template struct SelectOnChangedCallback { using Type = void; }; 174 | 175 | template 176 | struct HasTypeCallbackData 177 | { 178 | template static std::true_type test(typename C::CallbackData *) ; 179 | template static std::false_type test(...); 180 | 181 | enum { value = !! decltype(test(0))() }; 182 | }; 183 | template struct SelectCallbackData { using Type = typename T::CallbackData; }; 184 | template struct SelectCallbackData { using Type = void; }; 185 | 186 | template 187 | struct HasTypeStorage 188 | { 189 | template static std::true_type test(typename C::Storage *) ; 190 | template static std::false_type test(...); 191 | 192 | enum { value = !! decltype(test(0))() }; 193 | }; 194 | template struct SelectStorage { using Type = typename T::Storage; }; 195 | template struct SelectStorage { using Type = Default; }; 196 | 197 | template 198 | struct HasTypeClassTypeSetter 199 | { 200 | template static std::true_type test(typename C::ClassTypeSetter *) ; 201 | template static std::false_type test(...); 202 | 203 | enum { value = !! decltype(test(0))() }; 204 | }; 205 | template struct SelectClassTypeSetter { using Type = typename T::ClassTypeSetter; }; 206 | template struct SelectClassTypeSetter { using Type = Default; }; 207 | 208 | 209 | } // namespace private_ 210 | 211 | } // namespace accessorpp 212 | 213 | #endif 214 | -------------------------------------------------------------------------------- /include/accessorpp/setter.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef ACCESSORPP_SETTER_H_578722158669 15 | #define ACCESSORPP_SETTER_H_578722158669 16 | 17 | #include "internal/typeutil_i.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace accessorpp { 24 | 25 | template 26 | class Setter 27 | { 28 | public: 29 | using Type = Type_; 30 | using ValueType = typename private_::GetUnderlyingType::Type; 31 | 32 | public: 33 | Setter() 34 | : setterFunc([](void *, const ValueType &) {}) 35 | { 36 | } 37 | 38 | template 39 | explicit Setter(U * address, 40 | typename std::enable_if::value>::type * = nullptr) 41 | : setterFunc([address](void *, const ValueType & value) { *address = (U)(value); }) 42 | { 43 | } 44 | 45 | template 46 | Setter(U C::* address, C * instance, 47 | typename std::enable_if::value>::type * = nullptr) 48 | : setterFunc([address, instance](void *, const ValueType & value) { instance->*address = (U)(value); }) 49 | { 50 | } 51 | 52 | template 53 | Setter(U C::* address, 54 | typename std::enable_if::value>::type * = nullptr) 55 | : setterFunc([address](void * instance, const ValueType & value) { static_cast(instance)->*address = (U)(value); }) 56 | { 57 | } 58 | 59 | template 60 | explicit Setter(F func, 61 | typename std::enable_if::value>::type * = nullptr) 62 | : setterFunc([func](void *, const ValueType & value) { func(value); }) 63 | { 64 | } 65 | 66 | template 67 | Setter(F func, C * instance, 68 | typename std::enable_if::value>::type * = nullptr) 69 | : setterFunc([func, instance](void *, const ValueType & value) { (instance->*func)(value); }) 70 | { 71 | } 72 | 73 | template 74 | Setter(F func, 75 | typename std::enable_if::value>::type * = nullptr) 76 | : setterFunc([func](void * instance, const ValueType & value) { 77 | (static_cast::ClassType *>(instance)->*func)(value); 78 | }) 79 | { 80 | } 81 | 82 | Setter(const Setter & other) 83 | : setterFunc(other.setterFunc) 84 | { 85 | } 86 | 87 | Setter(Setter && other) 88 | : setterFunc(std::move(other.setterFunc)) 89 | { 90 | } 91 | 92 | Setter & operator = (const Setter & other) { 93 | setterFunc = other.setterFunc; 94 | return *this; 95 | } 96 | 97 | Setter & operator = (Setter && other) { 98 | setterFunc = std::move(other.setterFunc); 99 | return *this; 100 | } 101 | 102 | Setter & operator = (const ValueType & value) { 103 | set(value); 104 | return *this; 105 | } 106 | 107 | void set(const ValueType & value, void * instance = nullptr) { 108 | setterFunc(instance, value); 109 | } 110 | 111 | private: 112 | std::function setterFunc; 113 | }; 114 | 115 | template 116 | struct IsSetter : std::false_type 117 | { 118 | }; 119 | 120 | template 121 | struct IsSetter > : std::true_type 122 | { 123 | }; 124 | 125 | template 126 | std::istream & operator >> (std::istream & stream, Setter & setter) 127 | { 128 | Type value; 129 | stream >> value; 130 | setter = value; 131 | return stream; 132 | } 133 | 134 | 135 | } // namespace accessorpp 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | eventpp library 2 | 3 | Copyright (C) 2018 Wang Qi (wqking) 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # accessorpp -- C++ library for data binding and property 2 | 3 | accessorpp is a C++ library for implementing property and data binding. 4 | 5 | ## Facts and features 6 | 7 | - **Powerful** 8 | - Support arbitrary data type as property or data binding. 9 | - Support various kinds of getter and setter, such as data pointer, member data pointer, member function, lambda function, etc. 10 | - Support event dispatching on changing data. 11 | - Configurable using policies. 12 | - **Robust** 13 | - Well tested. Backed by unit tests. 14 | - **Flexible and easy to use** 15 | - Header only, no source file, no need to build. Does not depend on other libraries. 16 | - Requires C++ 11. 17 | - Written in portable and standard C++, no hacks or quirks. 18 | 19 | ## License 20 | 21 | Apache License, Version 2.0 22 | 23 | ## Version 0.1.0 24 | ![CI](https://github.com/wqking/accessorpp/workflows/CI/badge.svg) 25 | 26 | ## Source code 27 | 28 | [https://github.com/wqking/accessorpp](https://github.com/wqking/accessorpp) 29 | 30 | ## Supported compilers 31 | 32 | Tested with MSVC 2022, MSVC 2019, MinGW (Msys) GCC 7.2, Ubuntu GCC 5.4. 33 | In brief, MSVC, GCC, Clang that has well support for C++11, or released after 2019, should be able to compile the library. 34 | 35 | ## Quick start 36 | 37 | ### Namespace 38 | 39 | `accessorpp` 40 | 41 | ### Use accessorpp in your project 42 | 43 | accessorpp is header only library. Just clone the source code, then add the 'include' folder inside accessorpp to your project, then you can use the library. 44 | You don't need to link to any source code. 45 | 46 | ### Using Accessor 47 | 48 | **Simple usage** 49 | 50 | ```c++ 51 | #include "accessorpp/accessor.h" 52 | accessorpp::Accessor accessor; 53 | // output 0 54 | std::cout << (int)accessor << std::endl; 55 | accessor = 5; 56 | // output 5, we don't need to cast to int explicitly 57 | // Accessor does the casting for stream operators automatically. 58 | std::cout << accessor << std::endl; 59 | ``` 60 | 61 | **Customized getter/setter** 62 | 63 | ```c++ 64 | accessorpp::Accessor accessor( 65 | // This is the getter 66 | [&accessor]() { 67 | return accessor.directGet(); 68 | }, 69 | // This is the setter, it limits the value not exceeding 5. 70 | [&accessor](int value) { 71 | if(value > 5) { 72 | value = 5; 73 | } 74 | accessor.directSet(value); 75 | } 76 | ); 77 | accessor = 3; 78 | // output 3 79 | std::cout << (int)accessor << std::endl; 80 | accessor = 6; 81 | // output 5 82 | std::cout << (int)accessor << std::endl; 83 | ``` 84 | 85 | **Handle on change events** 86 | 87 | ```c++ 88 | struct Policies { 89 | using OnChangingCallback = std::function; 90 | using OnChangedCallback = std::function; 91 | }; 92 | 93 | using AccessorType = accessorpp::Accessor< 94 | int, 95 | Policies 96 | >; 97 | AccessorType accessor; 98 | accessor.onChanging() = [&accessor](const int newValue) { 99 | std::cout << "onChanging: new value = " << newValue << " old value = " << accessor << std::endl; 100 | }; 101 | accessor.onChanged() = [&accessor]() { 102 | std::cout << "onChanged: new value = " << accessor << std::endl; 103 | }; 104 | 105 | // output 106 | // onChanging: new value = 5 old value = 0 107 | // onChanged: new value = 5 108 | accessor = 5; 109 | 110 | // output 111 | // onChanging: new value = 38 old value = 5 112 | // onChanged: new value = 38 113 | accessor = 38; 114 | ``` 115 | 116 | 117 | ## Documentations 118 | 119 | * [Tutorial](doc/tutorial.md) 120 | * [Accessor](doc/accessor.md) 121 | * [Getter](doc/getter.md) 122 | * [Setter](doc/setter.md) 123 | 124 | ## Motivations 125 | 126 | Back to more than 10 years ago, in my (wqking) another monster library cpgf, I added accessor as a sub library, which serves for and binds to cpgf. Now this is a new independent library, with beautiful C++11 syntax, concise and easy to use. 127 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(accessorpptest) 2 | 3 | cmake_minimum_required(VERSION 3.2) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set(THIRDPARTY_PATH ../../thirdparty) 8 | 9 | include_directories(../include) 10 | 11 | if(MSVC) 12 | add_definitions(/W4) 13 | else() 14 | add_definitions(-Wall -Wextra -Wpedantic) 15 | endif() 16 | 17 | enable_testing() 18 | add_subdirectory(unittest) 19 | add_subdirectory(tutorial) 20 | add_subdirectory(benchmark) 21 | -------------------------------------------------------------------------------- /tests/benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET_BENCHMARK benchmark) 2 | 3 | set(SRC_BENCHMARK 4 | testmain.cpp 5 | b1_accessor.cpp 6 | ) 7 | 8 | add_executable( 9 | ${TARGET_BENCHMARK} 10 | ${SRC_BENCHMARK} 11 | ) 12 | 13 | set(THREADS_PREFER_PTHREAD_FLAG ON) 14 | find_package(Threads REQUIRED) 15 | target_link_libraries(${TARGET_BENCHMARK} Threads::Threads) 16 | 17 | -------------------------------------------------------------------------------- /tests/benchmark/b1_accessor.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | static volatile int intValue = 0; 18 | static volatile int intValue2 = 0; 19 | 20 | TEST_CASE("b1, accessor vs get/set directly") 21 | { 22 | constexpr int iterateCount = 1000 * 1000 * 1000; 23 | 24 | { 25 | const uint64_t cppTime = measureElapsedTime([iterateCount]() { 26 | for(int i = 0; i < iterateCount; ++i) { 27 | intValue2 = intValue; 28 | } 29 | }); 30 | accessorpp::Accessor accessor; 31 | const uint64_t accessorTime = measureElapsedTime([iterateCount, &accessor]() { 32 | for(int i = 0; i < iterateCount; ++i) { 33 | intValue2 = accessor; 34 | } 35 | }); 36 | std::cout << "Get int value: native = " << cppTime << " accessor = " << accessorTime << std::endl; 37 | 38 | } 39 | 40 | { 41 | const uint64_t cppTime = measureElapsedTime([iterateCount]() { 42 | for(int i = 0; i < iterateCount; ++i) { 43 | intValue = i; 44 | } 45 | }); 46 | accessorpp::Accessor accessor; 47 | const uint64_t accessorTime = measureElapsedTime([iterateCount, &accessor]() { 48 | for(int i = 0; i < iterateCount; ++i) { 49 | accessor = i; 50 | } 51 | }); 52 | std::cout << "Set int value: native = " << cppTime << " accessor = " << accessorTime << std::endl; 53 | 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /tests/benchmark/test.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef TEST_H 15 | #define TEST_H 16 | 17 | #include "../catch.hpp" 18 | 19 | #include 20 | #include 21 | 22 | template 23 | uint64_t measureElapsedTime(F f) 24 | { 25 | std::chrono::steady_clock::time_point t = std::chrono::steady_clock::now(); 26 | f(); 27 | return std::chrono::duration_cast(std::chrono::steady_clock::now() - t).count(); 28 | } 29 | 30 | #if defined(_MSC_VER) 31 | #define NON_INLINE __declspec(noinline) 32 | #else 33 | // gcc 34 | #define NON_INLINE __attribute__((noinline)) 35 | #endif 36 | 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /tests/benchmark/testmain.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 15 | #include "test.h" 16 | -------------------------------------------------------------------------------- /tests/build/makefile: -------------------------------------------------------------------------------- 1 | CACHE_DIR = temp_cache 2 | CMAKE = cmake ../.. $(CMAKE_FLAGS) 3 | 4 | MK_DIR = @cmake -E make_directory 5 | CH_DIR = @cmake -E chdir 6 | ECHO = @cmake -E echo 7 | 8 | MK_CACHE = $(MK_DIR) $(CACHE_DIR) 9 | EXEC_BUILD = $(CH_DIR) $(CACHE_DIR) 10 | 11 | PROJECT=eventpptest 12 | 13 | PROJECT_PREFIX = project 14 | 15 | SUPPORT_MAKES = linux mingw mingw_debug msys nmake vc17 vc15 16 | 17 | none: needcmake 18 | $(ECHO) "Usage:" 19 | $(ECHO) " make MakeType [CMAKE_FLAGS='flags passed to cmake']" 20 | $(ECHO) "or" 21 | $(ECHO) " nmake MakeType" 22 | $(ECHO) "if MS VC is used" 23 | $(ECHO) 24 | $(ECHO) "Available MakeType" 25 | $(ECHO) " $(SUPPORT_MAKES)" 26 | $(ECHO) 27 | $(ECHO) " linux Generate Linux/Unix makefile and then use GCC make to build." 28 | $(ECHO) " mingw Generate MinGW makefile and then use mingw32-make to build (release version)." 29 | $(ECHO) " mingw_debug Generate MinGW makefile and then use mingw32-make to build (debug version)." 30 | $(ECHO) " mingw_coverage Generate MinGW makefile and then use mingw32-make to build and generate code coverage report." 31 | $(ECHO) " msys Generate MSys makefile and then use make to build (release version)." 32 | $(ECHO) " nmake Generate Microsoft VC makefile and then use nmake to build." 33 | $(ECHO) " vc22 Generate project files for Microsoft VC 2022. No auto build. You need to open the project in VC IDE then build." 34 | $(ECHO) " vc19 Generate project files for Microsoft VC 2019. No auto build. You need to open the project in VC IDE then build." 35 | $(ECHO) " vc17 Generate project files for Microsoft VC 2017. No auto build. You need to open the project in VC IDE then build." 36 | $(ECHO) " vc15 Generate project files for Microsoft VC 2015. No auto build. You need to open the project in VC IDE then build." 37 | $(ECHO) " auto Auto detect the compiler and make environment and then use make to build. NOT recommend." 38 | 39 | needcmake: 40 | $(ECHO) 41 | 42 | auto: needcmake 43 | $(MK_CACHE)_auto 44 | $(CH_DIR) $(CACHE_DIR)_auto $(CMAKE) 45 | $(EXEC_BUILD)_auto make $(TARGET) 46 | 47 | mingw: needcmake 48 | $(MK_CACHE)_mingw 49 | $(CH_DIR) $(CACHE_DIR)_mingw $(CMAKE) -DCMAKE_BUILD_TYPE=Release -G"MinGW Makefiles" 50 | $(EXEC_BUILD)_mingw mingw32-make $(TARGET) 51 | 52 | mingw_debug: needcmake 53 | $(MK_CACHE)_mingw_debug 54 | $(CH_DIR) $(CACHE_DIR)_mingw_debug $(CMAKE) -DCMAKE_BUILD_TYPE=Debug -G"MinGW Makefiles" 55 | $(EXEC_BUILD)_mingw_debug mingw32-make $(TARGET) 56 | 57 | mingw_coverage: needcmake 58 | $(MK_CACHE)_mingw_coverage 59 | $(MK_DIR) ../coverage 60 | @cmake -E remove $(CACHE_DIR)_mingw_coverage/unittest/CMakeFiles/unittest.dir/*.gcov 61 | @cmake -E remove ../coverage/* 62 | $(CH_DIR) $(CACHE_DIR)_mingw_coverage $(CMAKE) -Dcoverage=True -G"MinGW Makefiles" 63 | $(EXEC_BUILD)_mingw_coverage mingw32-make $(TARGET) 64 | $(ECHO) "Running tests..." 65 | $(CH_DIR) $(CACHE_DIR)_mingw_coverage/unittest unittest 66 | $(CH_DIR) $(CACHE_DIR)_mingw_coverage/unittest/CMakeFiles/unittest.dir gcov -b ./*.cpp.gcno 67 | $(CH_DIR) ../coverage gcovr -r ../.. --html --html-details -o coverage.html 68 | 69 | msys: needcmake 70 | $(MK_CACHE)_msys 71 | $(CH_DIR) $(CACHE_DIR)_msys $(CMAKE) -DCMAKE_BUILD_TYPE=Release -G"MSYS Makefiles" 72 | $(EXEC_BUILD)_msys make $(TARGET) 73 | 74 | msys_debug: needcmake 75 | $(MK_CACHE)_msys_debug 76 | $(CH_DIR) $(CACHE_DIR)_msys_debug $(CMAKE) -DCMAKE_BUILD_TYPE=Debug -G"MSYS Makefiles" 77 | $(EXEC_BUILD)_msys_debug make $(TARGET) 78 | 79 | nmake: needcmake 80 | $(ECHO) NOTE: ***************************** 81 | $(ECHO) NOTE: If cmake raises errors, try run this in Visual Studio Command Prompt from the VS package. 82 | $(ECHO) NOTE: ***************************** 83 | $(MK_CACHE)_nmake 84 | $(CH_DIR) $(CACHE_DIR)_nmake $(CMAKE) -G"NMake Makefiles" 85 | $(EXEC_BUILD)_nmake nmake $(TARGET) 86 | 87 | linux: needcmake 88 | $(MK_CACHE)_linux 89 | $(CH_DIR) $(CACHE_DIR)_linux $(CMAKE) -DCMAKE_BUILD_TYPE=Release -G"Unix Makefiles" 90 | $(EXEC_BUILD)_linux make $(TARGET) 91 | 92 | linux_debug: needcmake 93 | $(MK_CACHE)_linux_debug 94 | $(CH_DIR) $(CACHE_DIR)_linux_debug $(CMAKE) -DCMAKE_BUILD_TYPE=Debug -G"Unix Makefiles" 95 | $(EXEC_BUILD)_linux_debug make $(TARGET) 96 | 97 | vc22: needcmake 98 | $(MK_DIR) $(PROJECT_PREFIX)_vc22 99 | $(CH_DIR) $(PROJECT_PREFIX)_vc22 $(CMAKE) -G "Visual Studio 17 2022" -A x64 100 | $(ECHO) Please open the solution $(PROJECT).sln in $(PROJECT_PREFIX)_vc22 in VC IDE. 101 | 102 | vc19: needcmake 103 | $(MK_DIR) $(PROJECT_PREFIX)_vc19 104 | $(CH_DIR) $(PROJECT_PREFIX)_vc19 $(CMAKE) -G "Visual Studio 16 2019" -A x64 105 | $(ECHO) Please open the solution $(PROJECT).sln in $(PROJECT_PREFIX)_vc19 in VC IDE. 106 | 107 | vc17: needcmake 108 | $(MK_DIR) $(PROJECT_PREFIX)_vc17 109 | $(CH_DIR) $(PROJECT_PREFIX)_vc17 $(CMAKE) -G"Visual Studio 15 2017 Win64" 110 | $(ECHO) Please open the solution $(PROJECT).sln in $(PROJECT_PREFIX)_vc17 in VC IDE. 111 | 112 | vc15: needcmake 113 | $(MK_DIR) $(PROJECT_PREFIX)_vc15 114 | $(CH_DIR) $(PROJECT_PREFIX)_vc15 $(CMAKE) -G"Visual Studio 14 Win64" 115 | $(ECHO) Please open the solution $(PROJECT).sln in $(PROJECT_PREFIX)_vc15 in VC IDE. 116 | 117 | .PHONY: clean 118 | 119 | -------------------------------------------------------------------------------- /tests/tutorial/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET_TUTORIAL tutorial) 2 | 3 | set(SRC_TUTORIAL 4 | tutorialmain.cpp 5 | tutorial_accessor.cpp 6 | tutorial_view_model_binding.cpp 7 | ) 8 | 9 | add_executable( 10 | ${TARGET_TUTORIAL} 11 | ${SRC_TUTORIAL} 12 | ) 13 | 14 | set(THREADS_PREFER_PTHREAD_FLAG ON) 15 | find_package(Threads REQUIRED) 16 | target_link_libraries(${TARGET_TUTORIAL} Threads::Threads) 17 | 18 | set_target_properties(${TARGET_TUTORIAL} PROPERTIES CXX_STANDARD 20) 19 | 20 | -------------------------------------------------------------------------------- /tests/tutorial/tutorial.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef TUTORIAL_H 15 | #define TUTORIAL_H 16 | 17 | #include "../catch.hpp" 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /tests/tutorial/tutorial_accessor.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Include the header 15 | #include "accessorpp/accessor.h" 16 | 17 | #include "tutorial.h" 18 | 19 | #include 20 | 21 | TEST_CASE("Accessor tutorial 1, basic") 22 | { 23 | std::cout << std::endl << "Accessor tutorial 1, basic" << std::endl; 24 | 25 | // The namespace is accessor. 26 | // The first parameter is the type of the underlying value. 27 | // By default, the accessor stores the value in internal storage. 28 | // The argument 0 is the initial value, it can be omitted 29 | // and access will use the default value, i.e, 0 for int, "" for string, etc. 30 | accessorpp::Accessor accessor(0); 31 | 32 | // The argument can be omitted, and the code will look lik, 33 | // accessorpp::Accessor accessor; 34 | 35 | // To obtain the underlying value, we can cast the accessor to the type 36 | std::cout 37 | << "Accessor tutorial 1: the default value should be 0, got " 38 | << (int)accessor 39 | << std::endl; 40 | 41 | // Now let's set a new value to the accessor using the assignment operator = . 42 | accessor = 3; 43 | // We can also call Accessor::get() to obtain the underlying value 44 | std::cout 45 | << "Accessor tutorial 1: the new value should be 3, got " 46 | << accessor.get() 47 | << std::endl; 48 | 49 | // We can also call Accessor::set() to set the underlying value. 50 | accessor.set(8); 51 | // Accessor supports stream operator directly, so we don't need to 52 | // obtain the underlying value. 53 | std::cout 54 | << "Accessor tutorial 1: the new value should be 8, got " 55 | << accessor 56 | << std::endl; 57 | } 58 | 59 | TEST_CASE("Accessor tutorial 2, customized getter/setter") 60 | { 61 | std::cout << std::endl << "Accessor tutorial 2, customized getter/setter" << std::endl; 62 | 63 | { 64 | // Let's create an accessor with customized getter and setter. 65 | // The first argument is the getter, the second is the setter. 66 | // Note: the code requires C++20 standard in MSVC, otherwise 67 | // it gives error "lambda capture variable not found". 68 | // The code works with GCC and Clang. 69 | accessorpp::Accessor accessor( 70 | // This is the getter 71 | [&accessor]() { return accessor.directGet(); }, 72 | // This is the setter, it multiplies the incoming value with 2 73 | // and store to the accessor, for fun. 74 | [&accessor](const int value) { accessor.directSet(value * 2); } 75 | ); 76 | accessor = 3; 77 | std::cout 78 | << "Accessor tutorial 2: the accessor is set with 3, now the value should be 6, got " 79 | << (int)accessor 80 | << std::endl; 81 | } 82 | 83 | { 84 | // Now let's create an accessor with default getter and customized setter. 85 | // The first argument is accessorpp::defaultGetter, it uses the getter supplied by the accessor, 86 | // the second is the setter, the third argument is the inital value. 87 | // Likewise, there is accessorpp::defaultSetter for the default getter. 88 | // This time we use the syntax that MSVC is happy with. 89 | accessorpp::Accessor * ptr; 90 | accessorpp::Accessor accessor( 91 | accessorpp::defaultGetter, 92 | // Must capture ptr as reference, otherwise it won't get the value set later. 93 | [&ptr](const int value) { ptr->directSet(value + 1); }, 94 | 5 95 | ); 96 | ptr = &accessor; 97 | std::cout 98 | << "Accessor tutorial 2: the initial accessor is 5, got " 99 | << (int)accessor 100 | << std::endl; 101 | accessor = 8; 102 | std::cout 103 | << "Accessor tutorial 2: the accessor is set with 8, now the value should be 9, got " 104 | << accessor 105 | << std::endl; 106 | } 107 | 108 | { 109 | // Now let's create a read-only accessor with default getter. 110 | // The first argument is accessorpp::defaultGetter, it uses the getter supplied by the accessor, 111 | // the second is accessorpp::noSetter, it means no setter available, thus the accessor is read-only. 112 | accessorpp::Accessor accessor( 113 | accessorpp::defaultGetter, 114 | accessorpp::noSetter, 115 | 5 116 | ); 117 | std::cout << std::boolalpha; 118 | std::cout 119 | << "Accessor tutorial 2: the initial accessor is 5, got " 120 | << accessor 121 | << std::endl; 122 | std::cout 123 | << "Accessor tutorial 2: isReadOnly() should be true, got " 124 | << accessor.isReadOnly() 125 | << std::endl; 126 | try { 127 | // Assigning to read-only accessor will throw exception std::logic_error 128 | accessor = 8; 129 | } 130 | catch(const std::logic_error &) { 131 | std::cout 132 | << "Accessor tutorial 2: set to the read-only accessor causes std::logic_error, we got here." 133 | << std::endl; 134 | } 135 | } 136 | } 137 | 138 | TEST_CASE("Accessor tutorial 3, external storage") 139 | { 140 | std::cout << std::endl << "Accessor tutorial 3, external storage" << std::endl; 141 | 142 | // To use external storage, we need to define the policies which is a struct. 143 | struct MyPolicies 144 | { 145 | // The type Storage determines how the value is stored. 146 | // If Storage is accessorpp::InternalStorage, the Accessor stores the value inside the Accessor. 147 | // If Storage is accessorpp::ExternalStorage, the Accessor doesn't store the value, and 148 | // how the underlying value is accessed completely depends on the getter and setter. 149 | using Storage = accessorpp::ExternalStorage; 150 | }; 151 | 152 | int value = 38; 153 | // Pass MyPolicies as the second template argument, and pass &value as the getter and setter. 154 | // The access will read from 'value', and set to 'value'. 155 | accessorpp::Accessor accessor(&value, &value); 156 | std::cout 157 | << "Accessor tutorial 3: the initial value is 38, accessor = " 158 | << accessor 159 | << std::endl; 160 | accessor = 8; 161 | std::cout 162 | << "Accessor tutorial 3: the accessor is set with 8, now both value and accessor should be 8, got" 163 | << " value = " << value << " and accessor = " << (int)accessor 164 | << std::endl; 165 | } 166 | 167 | TEST_CASE("Accessor tutorial 4, on change event") 168 | { 169 | std::cout << std::endl << "Accessor tutorial 4, on change event" << std::endl; 170 | 171 | // To use onChanging and onChanged events, we need to define the policies which is a struct. 172 | struct MyPolicies 173 | { 174 | // Use std::function as OnChangingCallback, 175 | // and leave OnChangedCallback default which will not trigger onChanged event. 176 | // Here we use std::function for demostration purpose, 177 | // you should use the CallbackList class in eventpp library, 178 | // https://github.com/wqking/eventpp 179 | using OnChangingCallback = std::function; 180 | }; 181 | accessorpp::Accessor accessor; 182 | // Assign the onChanging event callback. If we use eventpp::CallbackList here, 183 | // we can add multiple callbacks to the event. 184 | accessor.onChanging() = [&accessor](const int newValue) { 185 | std::cout << "Accessor tutorial 4: the onChanging event got new value " << newValue 186 | << " and old value is " << accessor << std::endl; 187 | }; 188 | 189 | // In onChanging, the newValue will be 8, and old value is 0 190 | accessor = 8; 191 | // In onChanging, the newValue will be 38, and old value is 8 192 | accessor = 38; 193 | } 194 | 195 | -------------------------------------------------------------------------------- /tests/tutorial/tutorial_view_model_binding.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Include the head 15 | #include "accessorpp/accessor.h" 16 | 17 | #include "tutorial.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // This is very naive implementation of a callback list. DON'T use it in your project. 25 | // You should use the CallbackList class in eventpp library, 26 | // https://github.com/wqking/eventpp 27 | class NaiveCallbackList 28 | { 29 | public: 30 | using Callback = std::function; 31 | 32 | void append(const Callback& callback) { 33 | callbackList.push_back(callback); 34 | } 35 | 36 | void operator () (const std::string & text, void * data) { 37 | for(auto& callback : callbackList) { 38 | callback(text, data); 39 | } 40 | } 41 | 42 | private: 43 | std::vector callbackList; 44 | }; 45 | 46 | class Model 47 | { 48 | private: 49 | struct MyPolicies 50 | { 51 | using OnChangedCallback = NaiveCallbackList; 52 | using CallbackData = void *; 53 | }; 54 | 55 | public: 56 | accessorpp::Accessor text; 57 | }; 58 | 59 | class EditView 60 | { 61 | public: 62 | void bindModel(Model* modelToBind) { 63 | model = modelToBind; 64 | model->text.onChanged().append([this](const std::string & text, void * data) { 65 | this->onModelChanged(text, data); 66 | }); 67 | } 68 | 69 | void userInput(const std::string & text) { 70 | displayedText = text; 71 | model->text.set(text, (void *)this); 72 | } 73 | 74 | const std::string & getDisplayedText() const { 75 | return displayedText; 76 | } 77 | 78 | private: 79 | void onModelChanged(const std::string & text, void * data) { 80 | if(data != this) { 81 | displayedText = text; 82 | } 83 | } 84 | 85 | private: 86 | Model* model; 87 | std::string displayedText; 88 | }; 89 | 90 | class LabelView 91 | { 92 | public: 93 | void bindModel(Model* modelToBind) { 94 | model = modelToBind; 95 | model->text.onChanged().append([this](const std::string & text, void * data) { 96 | this->onModelChanged(text, data); 97 | }); 98 | } 99 | 100 | const std::string & getDisplayedText() const { 101 | return displayedText; 102 | } 103 | 104 | private: 105 | void onModelChanged(const std::string & text, void * /*data*/) { 106 | displayedText = "Hello, " + text; 107 | } 108 | 109 | private: 110 | Model* model; 111 | std::string displayedText; 112 | }; 113 | 114 | class Application 115 | { 116 | public: 117 | Application() { 118 | editView.bindModel(&model); 119 | labelView.bindModel(&model); 120 | } 121 | 122 | void run() { 123 | std::cout << "Running tutorial view model binding" << std::endl; 124 | 125 | std::cout << "User typed 'world'" << std::endl; 126 | editView.userInput("world"); 127 | std::cout << "Now EditView displays '" << editView.getDisplayedText() 128 | << "'. LabelView displays '" << labelView.getDisplayedText() << "'" 129 | << std::endl; 130 | 131 | std::cout << "User typed 'accessor is good'" << std::endl; 132 | editView.userInput("accessor is good"); 133 | std::cout << "Now EditView displays '" << editView.getDisplayedText() 134 | << "'. LabelView displays '" << labelView.getDisplayedText() << "'" 135 | << std::endl; 136 | 137 | std::cout << "Set text as 'set by code'" << std::endl; 138 | model.text = "set by code"; 139 | std::cout << "Now EditView displays '" << editView.getDisplayedText() 140 | << "'. LabelView displays '" << labelView.getDisplayedText() << "'" 141 | << std::endl; 142 | } 143 | 144 | private: 145 | Model model; 146 | EditView editView; 147 | LabelView labelView; 148 | }; 149 | 150 | TEST_CASE("Tutorial view model binding") 151 | { 152 | Application app; 153 | app.run(); 154 | } 155 | -------------------------------------------------------------------------------- /tests/tutorial/tutorialmain.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 15 | #include "tutorial.h" 16 | -------------------------------------------------------------------------------- /tests/unittest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TARGET_TEST unittest) 2 | 3 | file(GLOB_RECURSE SRC_TEST "./*.cpp") 4 | 5 | add_executable( 6 | ${TARGET_TEST} 7 | ${SRC_TEST} 8 | ) 9 | 10 | set(THREADS_PREFER_PTHREAD_FLAG ON) 11 | find_package(Threads REQUIRED) 12 | target_link_libraries(${TARGET_TEST} Threads::Threads) 13 | 14 | set_target_properties(${TARGET_TEST} PROPERTIES CXX_STANDARD 20) 15 | 16 | if(CMAKE_COMPILER_IS_GNUCXX) 17 | if(coverage) 18 | set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") 19 | endif() 20 | endif() 21 | 22 | add_test(NAME ${TARGET_TEST} COMMAND ${TARGET_TEST}) 23 | -------------------------------------------------------------------------------- /tests/unittest/test.h: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #ifndef TEST_H 15 | #define TEST_H 16 | 17 | #include "../catch.hpp" 18 | 19 | template 20 | struct EraseArgs1 21 | { 22 | template 23 | explicit EraseArgs1(const C & callable) : callable(callable) 24 | { 25 | } 26 | 27 | template 28 | ReturnType operator() (First &&, Args && ...args) 29 | { 30 | callable(std::forward(args)...); 31 | } 32 | 33 | Callable callable; 34 | }; 35 | 36 | template 37 | EraseArgs1 eraseArgs1(const Callable & callable) 38 | { 39 | return EraseArgs1(callable); 40 | } 41 | 42 | template 43 | bool checkAllWeakPtrAreFreed(const T & nodeList) 44 | { 45 | for(const auto & node : nodeList) { 46 | if(node.lock()) { 47 | return false; 48 | } 49 | } 50 | 51 | return true; 52 | } 53 | 54 | // Can be converted from int implicitly 55 | struct FromInt 56 | { 57 | FromInt() : value(0) {} 58 | FromInt(const int value) : value(value) {} 59 | 60 | int value; 61 | }; 62 | 63 | // Can convert to int implicitly 64 | struct ToInt 65 | { 66 | ToInt() : value(0) {} 67 | explicit ToInt(const int value) : value(value) {} 68 | 69 | operator int() const { return value; } 70 | 71 | int value; 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_basic.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | #include 18 | #include 19 | 20 | TEST_CASE("Accessor, ctor") 21 | { 22 | accessorpp::Accessor accessor1; 23 | REQUIRE(accessor1.get() == 0); 24 | 25 | accessor1 = 3; 26 | REQUIRE(accessor1.get() == 3); 27 | 28 | accessorpp::Accessor accessor2(accessor1); 29 | REQUIRE(accessor2.get() == 3); 30 | accessor2 = 5; 31 | REQUIRE(accessor1.get() == 3); 32 | REQUIRE(accessor2.get() == 5); 33 | } 34 | 35 | TEST_CASE("Accessor, int *, default storage, variable") 36 | { 37 | accessorpp::Accessor accessor; 38 | REQUIRE(accessor.get() == nullptr); 39 | 40 | int value1; 41 | accessor = &value1; 42 | REQUIRE(accessor.get() == &value1); 43 | 44 | int value2; 45 | accessor = &value2; 46 | REQUIRE(accessor.get() == &value2); 47 | } 48 | 49 | TEST_CASE("Accessor, void *, default storage, variable") 50 | { 51 | accessorpp::Accessor accessor; 52 | REQUIRE(accessor.get() == nullptr); 53 | 54 | int value1; 55 | accessor = &value1; 56 | REQUIRE(accessor.get() == &value1); 57 | 58 | int value2; 59 | accessor = &value2; 60 | REQUIRE(accessor.get() == &value2); 61 | } 62 | 63 | TEST_CASE("Accessor, int, default storage, variable") 64 | { 65 | accessorpp::Accessor accessor; 66 | REQUIRE(accessor.get() == 0); 67 | 68 | accessor = 3; 69 | REQUIRE(accessor.get() == 3); 70 | 71 | accessor = 8; 72 | REQUIRE(accessor.get() == 8); 73 | } 74 | 75 | TEST_CASE("Accessor, const int &, default storage, variable") 76 | { 77 | accessorpp::Accessor accessor; 78 | REQUIRE(accessor.get() == 0); 79 | 80 | accessor = 3; 81 | REQUIRE(accessor.get() == 3); 82 | 83 | accessor = 8; 84 | REQUIRE(accessor.get() == 8); 85 | } 86 | 87 | TEST_CASE("Accessor, int &, default storage, variable") 88 | { 89 | accessorpp::Accessor accessor; 90 | REQUIRE(accessor.get() == 0); 91 | 92 | int value = 3; 93 | accessor = value; 94 | REQUIRE(accessor.get() == 3); 95 | 96 | value = 8; 97 | accessor = value; 98 | REQUIRE(accessor.get() == 8); 99 | } 100 | 101 | TEST_CASE("Accessor, bool, default storage, variable") 102 | { 103 | accessorpp::Accessor accessor; 104 | REQUIRE(accessor.get() == false); 105 | 106 | accessor = true; 107 | REQUIRE(accessor.get() == true); 108 | 109 | accessor = false; 110 | REQUIRE(accessor.get() == false); 111 | } 112 | 113 | TEST_CASE("Accessor, std::string, default storage, variable") 114 | { 115 | accessorpp::Accessor accessor; 116 | REQUIRE(accessor.get() == ""); 117 | 118 | accessor = "Hello"; 119 | REQUIRE(accessor.get() == "Hello"); 120 | 121 | accessor = "World"; 122 | REQUIRE(accessor == "World"); 123 | } 124 | 125 | struct MyValue 126 | { 127 | MyValue(const int value = 0) 128 | : value(value) { 129 | } 130 | 131 | int getValue() const { 132 | return value; 133 | } 134 | 135 | const int & getCref() const { 136 | return value; 137 | } 138 | 139 | int & getRef() { 140 | return value; 141 | } 142 | 143 | void setValue(const int newValue) { 144 | value = newValue; 145 | } 146 | 147 | void setConst(const int newValue) const { 148 | const_cast(this)->value = newValue; 149 | } 150 | 151 | int value; 152 | }; 153 | 154 | TEST_CASE("Accessor, MyValue, default storage") 155 | { 156 | accessorpp::Accessor accessor; 157 | REQUIRE(accessor.get().getValue() == 0); 158 | 159 | accessor = MyValue(3); 160 | REQUIRE(accessor.get().getValue() == 3); 161 | 162 | accessor = MyValue(8); 163 | REQUIRE(accessor.get().getValue() == 8); 164 | } 165 | 166 | TEST_CASE("Accessor, int, default storage, variable, customized getter/setter") 167 | { 168 | int getCount = 0; 169 | int setCount = 0; 170 | accessorpp::Accessor accessor; 171 | accessor.setGetter([&accessor, &getCount]() { 172 | ++getCount; 173 | return accessor.directGet(); 174 | }); 175 | accessor.setSetter([&accessor, &setCount](const int value) { 176 | ++setCount; 177 | accessor.directSet(value); 178 | }); 179 | 180 | REQUIRE(accessor.get() == 0); 181 | REQUIRE(getCount == 1); 182 | REQUIRE(setCount == 0); 183 | 184 | accessor = 3; 185 | REQUIRE(getCount == 1); 186 | REQUIRE(setCount == 1); 187 | REQUIRE(accessor.get() == 3); 188 | REQUIRE(getCount == 2); 189 | REQUIRE(setCount == 1); 190 | 191 | accessor = 8; 192 | REQUIRE(getCount == 2); 193 | REQUIRE(setCount == 2); 194 | REQUIRE(accessor.get() == 8); 195 | REQUIRE(getCount == 3); 196 | REQUIRE(setCount == 2); 197 | } 198 | 199 | struct NoStoragePolicies 200 | { 201 | using Storage = accessorpp::ExternalStorage; 202 | }; 203 | 204 | TEST_CASE("Accessor, int, ExternalStorage, variable") 205 | { 206 | int value{}; 207 | SECTION("Use ctor") { 208 | accessorpp::Accessor accessor(&value, &value); 209 | REQUIRE(! accessorpp::Accessor::internalStorage); 210 | 211 | REQUIRE(accessor.get() == 0); 212 | 213 | accessor = 3; 214 | REQUIRE(accessor.get() == 3); 215 | 216 | accessor = 8; 217 | REQUIRE(accessor.get() == 8); 218 | } 219 | 220 | SECTION("Use createAccessor") { 221 | auto accessor(accessorpp::createAccessor(&value, &value, NoStoragePolicies())); 222 | REQUIRE(! accessorpp::Accessor::internalStorage); 223 | 224 | REQUIRE(accessor.get() == 0); 225 | 226 | accessor = 3; 227 | REQUIRE(accessor.get() == 3); 228 | 229 | accessor = 8; 230 | REQUIRE(accessor.get() == 8); 231 | } 232 | 233 | SECTION("Use createAccessor") { 234 | auto accessor(accessorpp::createAccessor(&value, &value, NoStoragePolicies())); 235 | REQUIRE(! accessorpp::Accessor::internalStorage); 236 | 237 | REQUIRE(accessor.get() == 0); 238 | 239 | accessor = 3; 240 | REQUIRE(accessor.get() == 3); 241 | 242 | accessor = 8; 243 | REQUIRE(accessor.get() == 8); 244 | } 245 | } 246 | 247 | TEST_CASE("Accessor, int, ExternalStorage, member, embed instance") 248 | { 249 | MyValue myValue; 250 | SECTION("Use ctor") { 251 | accessorpp::Accessor accessor(&MyValue::value, &myValue, &MyValue::value, &myValue); 252 | REQUIRE(accessor.get() == 0); 253 | 254 | accessor = 3; 255 | REQUIRE(accessor.get() == 3); 256 | 257 | accessor = 8; 258 | REQUIRE(accessor.get() == 8); 259 | } 260 | 261 | SECTION("Use createAccessor") { 262 | auto accessor(accessorpp::createAccessor(&MyValue::value, &myValue, &MyValue::value, &myValue, NoStoragePolicies())); 263 | REQUIRE(accessor.get() == 0); 264 | 265 | accessor = 3; 266 | REQUIRE(accessor.get() == 3); 267 | 268 | accessor = 8; 269 | REQUIRE(accessor.get() == 8); 270 | } 271 | 272 | SECTION("Use createAccessor") { 273 | auto accessor(accessorpp::createAccessor(&MyValue::value, &myValue, &MyValue::value, &myValue, NoStoragePolicies())); 274 | REQUIRE(accessor.get() == 0); 275 | 276 | accessor = 3; 277 | REQUIRE(accessor.get() == 3); 278 | 279 | accessor = 8; 280 | REQUIRE(accessor.get() == 8); 281 | } 282 | } 283 | 284 | TEST_CASE("Accessor, int, ExternalStorage, member, pass instance") 285 | { 286 | MyValue myValue; 287 | accessorpp::Accessor accessor(&MyValue::value, &MyValue::value); 288 | REQUIRE(accessor.get(&myValue) == 0); 289 | 290 | accessor.set(3, &myValue); 291 | REQUIRE(accessor.get(&myValue) == 3); 292 | 293 | accessor.set(8, &myValue); 294 | REQUIRE(accessor.get(&myValue) == 8); 295 | } 296 | 297 | TEST_CASE("Accessor, int, ExternalStorage, member getValue() setValue(), embed instance") 298 | { 299 | MyValue myValue; 300 | accessorpp::Accessor accessor(&MyValue::getValue, &myValue, &MyValue::setValue, &myValue); 301 | REQUIRE(accessor.get() == 0); 302 | 303 | accessor = 3; 304 | REQUIRE(accessor.get() == 3); 305 | 306 | accessor = 8; 307 | REQUIRE(accessor.get() == 8); 308 | } 309 | 310 | TEST_CASE("Accessor, int, ExternalStorage, member getValue() setValue(), pass instance") 311 | { 312 | MyValue myValue; 313 | accessorpp::Accessor accessor(&MyValue::getValue, &MyValue::setValue); 314 | REQUIRE(accessor.get(&myValue) == 0); 315 | 316 | accessor.set(3, &myValue); 317 | REQUIRE(accessor.get(&myValue) == 3); 318 | 319 | accessor.set(8, &myValue); 320 | REQUIRE(accessor.get(&myValue) == 8); 321 | } 322 | 323 | TEST_CASE("Accessor, default storage, read only") 324 | { 325 | using Accessor = accessorpp::Accessor; 326 | 327 | { 328 | Accessor accessor(accessorpp::defaultGetter, accessorpp::noSetter); 329 | REQUIRE(accessor == 0); 330 | REQUIRE(accessor.isReadOnly()); 331 | CHECK_THROWS(accessor = 5); 332 | } 333 | 334 | { 335 | auto accessor(accessorpp::createReadOnlyAccessor(accessorpp::defaultGetter)); 336 | REQUIRE(accessor == 0); 337 | REQUIRE(accessor.isReadOnly()); 338 | CHECK_THROWS(accessor = 5); 339 | } 340 | 341 | { 342 | Accessor accessor(accessorpp::defaultGetter, accessorpp::noSetter, 3); 343 | REQUIRE(accessor == 3); 344 | REQUIRE(accessor.isReadOnly()); 345 | CHECK_THROWS(accessor = 5); 346 | } 347 | 348 | { 349 | int n = 5; 350 | Accessor accessor(&n, accessorpp::noSetter, 3); 351 | REQUIRE(accessor == 5); 352 | REQUIRE(accessor.isReadOnly()); 353 | CHECK_THROWS(accessor = 6); 354 | } 355 | 356 | { 357 | Accessor accessor(accessorpp::defaultGetter, accessorpp::defaultSetter, 3); 358 | REQUIRE(accessor == 3); 359 | REQUIRE(! accessor.isReadOnly()); 360 | accessor = 5; 361 | REQUIRE(accessor == 5); 362 | } 363 | } 364 | 365 | TEST_CASE("This is the play camp for sample code in the document") 366 | { 367 | struct Policies { 368 | using OnChangingCallback = std::function; 369 | using OnChangedCallback = std::function; 370 | }; 371 | 372 | using AccessorType = accessorpp::Accessor< 373 | int, 374 | Policies 375 | >; 376 | AccessorType accessor; 377 | accessor.onChanging() = [&accessor](const int newValue) { 378 | std::cout << "onChanging: new value = " << newValue << " old value = " << accessor << std::endl; 379 | }; 380 | accessor.onChanged() = [&accessor]() { 381 | std::cout << "onChanged: new value = " << accessor << std::endl; 382 | }; 383 | 384 | // output 385 | // onChanging: new value = 5 old value = 0 386 | // onChanged: new value = 5 387 | accessor = 5; 388 | 389 | // output 390 | // onChanging: new value = 38 old value = 5 391 | // onChanged: new value = 38 392 | accessor = 38; 393 | } 394 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_callback.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | #include 18 | #include 19 | 20 | TEST_CASE("Accessor, callback") 21 | { 22 | struct Policies { 23 | using OnChangingCallback = std::function; 24 | using OnChangedCallback = std::function; 25 | }; 26 | 27 | using AccessorType = accessorpp::Accessor< 28 | const int &, 29 | Policies 30 | >; 31 | struct ValuePair 32 | { 33 | int newValue; 34 | int oldValue; 35 | }; 36 | ValuePair changingValue {}; 37 | ValuePair changedValue {}; 38 | AccessorType accessor; 39 | accessor.onChanging() = [&changingValue, &accessor](const int newValue) { 40 | changingValue.newValue = newValue; 41 | changingValue.oldValue = accessor; 42 | }; 43 | accessor.onChanged() = [&changedValue, &accessor]() { 44 | changedValue.newValue = accessor; 45 | }; 46 | REQUIRE(changingValue.newValue == 0); 47 | REQUIRE(changingValue.oldValue == 0); 48 | REQUIRE(changedValue.newValue == 0); 49 | REQUIRE(changedValue.oldValue == 0); 50 | 51 | accessor = 3; 52 | REQUIRE(changingValue.newValue == 3); 53 | REQUIRE(changingValue.oldValue == 0); 54 | REQUIRE(changedValue.newValue == 3); 55 | REQUIRE(changedValue.oldValue == 0); 56 | 57 | accessor = 8; 58 | REQUIRE(changingValue.newValue == 8); 59 | REQUIRE(changingValue.oldValue == 3); 60 | REQUIRE(changedValue.newValue == 8); 61 | REQUIRE(changedValue.oldValue == 0); 62 | } 63 | 64 | TEST_CASE("Accessor, callback, CallbackData") 65 | { 66 | struct Policies { 67 | using OnChangingCallback = std::function; 68 | using OnChangedCallback = std::function; 69 | using CallbackData = std::string; 70 | }; 71 | 72 | using AccessorType = accessorpp::Accessor< 73 | const int &, 74 | Policies 75 | >; 76 | 77 | struct ValuePair 78 | { 79 | int newValue; 80 | int oldValue; 81 | std::string context; 82 | }; 83 | ValuePair changingValue {}; 84 | ValuePair changedValue {}; 85 | AccessorType accessor; 86 | accessor.onChanging() = [&changingValue, &accessor](const int newValue, const std::string & context) { 87 | changingValue.newValue = newValue; 88 | changingValue.oldValue = accessor; 89 | changingValue.context = context; 90 | }; 91 | accessor.onChanged() = [&changedValue, &accessor]() { 92 | changedValue.newValue = accessor; 93 | }; 94 | REQUIRE(changingValue.newValue == 0); 95 | REQUIRE(changingValue.oldValue == 0); 96 | REQUIRE(changedValue.newValue == 0); 97 | REQUIRE(changedValue.oldValue == 0); 98 | 99 | accessor = 3; 100 | REQUIRE(changingValue.newValue == 3); 101 | REQUIRE(changingValue.oldValue == 0); 102 | REQUIRE(changingValue.context == ""); 103 | REQUIRE(changedValue.newValue == 3); 104 | REQUIRE(changedValue.oldValue == 0); 105 | 106 | accessor.setWithCallbackData(8, "Hello"); 107 | REQUIRE(changingValue.newValue == 8); 108 | REQUIRE(changingValue.oldValue == 3); 109 | REQUIRE(changingValue.context == "Hello"); 110 | REQUIRE(changedValue.newValue == 8); 111 | REQUIRE(changedValue.oldValue == 0); 112 | } 113 | 114 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_default_no_getter_setter.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | struct MyValue 18 | { 19 | MyValue(const int value = 0) 20 | : value(value) { 21 | } 22 | 23 | int getValue() const { 24 | return value; 25 | } 26 | 27 | const int & getCref() const { 28 | return value; 29 | } 30 | 31 | int & getRef() { 32 | return value; 33 | } 34 | 35 | void setValue(const int newValue) { 36 | value = newValue; 37 | } 38 | 39 | void setConst(const int newValue) const { 40 | const_cast(this)->value = newValue; 41 | } 42 | 43 | int value; 44 | }; 45 | 46 | TEST_CASE("Accessor, default storage, defaultGetter, setter") 47 | { 48 | using Accessor = accessorpp::Accessor; 49 | 50 | { 51 | Accessor * ptr; 52 | Accessor accessor(accessorpp::defaultGetter, [&ptr](const int newValue) { 53 | ptr->directSet(newValue * 2); 54 | }); 55 | ptr = &accessor; 56 | REQUIRE(accessor == 0); 57 | REQUIRE(! accessor.isReadOnly()); 58 | accessor = 5; 59 | REQUIRE(accessor == 10); 60 | } 61 | } 62 | 63 | TEST_CASE("Accessor, default storage, getter, defaultSetter") 64 | { 65 | using Accessor = accessorpp::Accessor; 66 | 67 | { 68 | // accessor always returns 5 69 | Accessor accessor(5, accessorpp::defaultSetter, 3); 70 | REQUIRE(accessor == 5); 71 | REQUIRE(! accessor.isReadOnly()); 72 | accessor = 8; // no effect 73 | REQUIRE(accessor == 5); 74 | } 75 | } 76 | 77 | TEST_CASE("Accessor, default storage, defaultGetter, noSetter") 78 | { 79 | using Accessor = accessorpp::Accessor; 80 | 81 | { 82 | Accessor accessor(accessorpp::defaultGetter, accessorpp::noSetter); 83 | REQUIRE(accessor == 0); 84 | REQUIRE(accessor.isReadOnly()); 85 | CHECK_THROWS(accessor = 5); 86 | } 87 | 88 | { 89 | Accessor accessor(accessorpp::defaultGetter, accessorpp::noSetter, 3); 90 | REQUIRE(accessor == 3); 91 | REQUIRE(accessor.isReadOnly()); 92 | CHECK_THROWS(accessor = 5); 93 | } 94 | } 95 | 96 | TEST_CASE("Accessor, default storage, getter, noSetter") 97 | { 98 | using Accessor = accessorpp::Accessor; 99 | 100 | { 101 | int n = 5; 102 | Accessor accessor(&n, accessorpp::noSetter, 3); 103 | REQUIRE(accessor == 5); 104 | REQUIRE(accessor.isReadOnly()); 105 | CHECK_THROWS(accessor = 6); 106 | } 107 | 108 | { 109 | MyValue myValue(8); 110 | Accessor accessor(Accessor::GetterType(&MyValue::getValue, &myValue), accessorpp::noSetter); 111 | REQUIRE(accessor == 8); 112 | REQUIRE(accessor.isReadOnly()); 113 | CHECK_THROWS(accessor = 6); 114 | } 115 | } 116 | 117 | TEST_CASE("Accessor, default storage, defaultGetter, defaultSetter") 118 | { 119 | using Accessor = accessorpp::Accessor; 120 | 121 | { 122 | Accessor accessor(accessorpp::defaultGetter, accessorpp::defaultSetter, 3); 123 | REQUIRE(accessor == 3); 124 | REQUIRE(! accessor.isReadOnly()); 125 | accessor = 5; 126 | REQUIRE(accessor == 5); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_object.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | #include 18 | #include 19 | 20 | namespace { 21 | 22 | struct MyClass 23 | { 24 | const std::string & getStr() const { 25 | return str; 26 | } 27 | 28 | void setStr(const std::string & s) { 29 | str = s; 30 | } 31 | 32 | std::string str; 33 | }; 34 | 35 | struct NoStoragePolicies 36 | { 37 | using Storage = accessorpp::ExternalStorage; 38 | }; 39 | 40 | TEST_CASE("Accessor, MyClass, const std::string &") 41 | { 42 | MyClass obj; 43 | //accessorpp::Accessor accessor(&MyClass::getStr, &MyClass::setStr); 44 | auto accessor = accessorpp::createAccessor(&MyClass::getStr, &MyClass::setStr, NoStoragePolicies()); 45 | 46 | REQUIRE(accessor.get(&obj) == ""); 47 | 48 | accessor.set("hello", &obj); 49 | REQUIRE(accessor.get(&obj) == "hello"); 50 | 51 | obj.str = "world"; 52 | REQUIRE(accessor.get(&obj) == "world"); 53 | } 54 | 55 | 56 | } // namespace 57 | 58 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_operator_binary.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | TEST_CASE("Accessor, int, binary operator +, -, *, /, %, &, |, ^, <<, >>") 18 | { 19 | accessorpp::Accessor accessor1; 20 | accessorpp::Accessor accessor2; 21 | 22 | accessor1 = 15; 23 | accessor2 = 3; 24 | REQUIRE(accessor1 == 15); 25 | REQUIRE(accessor2 == 3); 26 | 27 | REQUIRE(accessor1 + accessor2 == 18); 28 | REQUIRE(accessor1 + 3 == 18); 29 | REQUIRE(accessor1 == 15); 30 | REQUIRE(accessor2 == 3); 31 | REQUIRE((accessor1 + accessor2).get() == 18); 32 | REQUIRE((accessor1 + 3).get() == 18); 33 | 34 | REQUIRE(accessor1 - accessor2 == 12); 35 | REQUIRE(accessor1 - 3 == 12); 36 | REQUIRE((accessor1 - accessor2).get() == 12); 37 | REQUIRE((accessor1 - 3).get() == 12); 38 | 39 | REQUIRE(accessor1 * accessor2 == 45); 40 | REQUIRE(accessor1 * 3== 45); 41 | REQUIRE((accessor1 * accessor2).get() == 45); 42 | REQUIRE((accessor1 * 3).get() == 45); 43 | 44 | REQUIRE(accessor1 / accessor2 == 5); 45 | REQUIRE(accessor1 / 3 == 5); 46 | REQUIRE((accessor1 / accessor2).get() == 5); 47 | REQUIRE((accessor1 / 3).get() == 5); 48 | 49 | accessor1 = 10; // 0b1010 50 | accessor2 = 6; // 0b0110 51 | REQUIRE(accessor1 % accessor2 == 4); 52 | REQUIRE(accessor1 % 6 == 4); 53 | REQUIRE((accessor1 % accessor2).get() == 4); 54 | REQUIRE((accessor1 % 6).get() == 4); 55 | 56 | REQUIRE((accessor1 & accessor2) == 2); 57 | REQUIRE((accessor1 & 6) == 2); 58 | REQUIRE((accessor1 & accessor2).get() == 2); 59 | REQUIRE((accessor1 & 6).get() == 2); 60 | 61 | REQUIRE((accessor1 | accessor2) == 14); 62 | REQUIRE((accessor1 | 6) == 14); 63 | REQUIRE((accessor1 | accessor2).get() == 14); 64 | REQUIRE((accessor1 | 6).get() == 14); 65 | 66 | REQUIRE((accessor1 ^ accessor2) == 12); 67 | REQUIRE((accessor1 ^ 6) == 12); 68 | REQUIRE((accessor1 ^ accessor2).get() == 12); 69 | REQUIRE((accessor1 ^ 6).get() == 12); 70 | 71 | accessor1 = 14; // 0b1110 72 | accessor2 = 2; 73 | REQUIRE(accessor1 << accessor2 == 56); 74 | REQUIRE(accessor1 << 2 == 56); 75 | REQUIRE((accessor1 << accessor2).get() == 56); 76 | REQUIRE((accessor1 << 2).get() == 56); 77 | 78 | REQUIRE(accessor1 >> accessor2 == 3); 79 | REQUIRE(accessor1 >> 2 == 3); 80 | REQUIRE((accessor1 >> accessor2).get() == 3); 81 | REQUIRE((accessor1 >> 2).get() == 3); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_operator_binary_assignment.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | TEST_CASE("Accessor, int, binary assignment operator +=") 18 | { 19 | accessorpp::Accessor accessor1; 20 | accessorpp::Accessor accessor2; 21 | 22 | accessor1 = 15; 23 | accessor2 = 3; 24 | 25 | accessor1 += accessor2; 26 | REQUIRE(accessor1 == 18); 27 | REQUIRE(accessor2 == 3); 28 | 29 | accessor1 += 3; 30 | REQUIRE(accessor1 == 21); 31 | } 32 | 33 | TEST_CASE("Accessor, int, binary assignment operator -=") 34 | { 35 | accessorpp::Accessor accessor1; 36 | accessorpp::Accessor accessor2; 37 | 38 | accessor1 = 15; 39 | accessor2 = 3; 40 | 41 | accessor1 -= accessor2; 42 | REQUIRE(accessor1 == 12); 43 | REQUIRE(accessor2 == 3); 44 | 45 | accessor1 -= 3; 46 | REQUIRE(accessor1 == 9); 47 | } 48 | 49 | TEST_CASE("Accessor, int, binary assignment operator *=") 50 | { 51 | accessorpp::Accessor accessor1; 52 | accessorpp::Accessor accessor2; 53 | 54 | accessor1 = 15; 55 | accessor2 = 3; 56 | 57 | accessor1 *= accessor2; 58 | REQUIRE(accessor1 == 45); 59 | REQUIRE(accessor2 == 3); 60 | 61 | accessor1 *= 3; 62 | REQUIRE(accessor1 == 135); 63 | } 64 | 65 | TEST_CASE("Accessor, int, binary assignment operator /=") 66 | { 67 | accessorpp::Accessor accessor1; 68 | accessorpp::Accessor accessor2; 69 | 70 | accessor1 = 45; 71 | accessor2 = 3; 72 | 73 | accessor1 /= accessor2; 74 | REQUIRE(accessor1 == 15); 75 | REQUIRE(accessor2 == 3); 76 | 77 | accessor1 /= 3; 78 | REQUIRE(accessor1 == 5); 79 | } 80 | 81 | TEST_CASE("Accessor, int, binary assignment operator %=") 82 | { 83 | accessorpp::Accessor accessor1; 84 | accessorpp::Accessor accessor2; 85 | 86 | accessor1 = 16; 87 | accessor2 = 3; 88 | 89 | accessor1 %= accessor2; 90 | REQUIRE(accessor1 == 1); 91 | REQUIRE(accessor2 == 3); 92 | 93 | accessor1 = 8; 94 | accessor1 %= 3; 95 | REQUIRE(accessor1 == 2); 96 | } 97 | 98 | TEST_CASE("Accessor, int, binary assignment operator &=") 99 | { 100 | accessorpp::Accessor accessor1; 101 | accessorpp::Accessor accessor2; 102 | 103 | accessor1 = 10; // 0b1010 104 | accessor2 = 6; // 0b0110 105 | 106 | accessor1 &= accessor2; 107 | REQUIRE(accessor1 == 2); 108 | REQUIRE(accessor2 == 6); 109 | 110 | accessor1 = 7; 111 | accessor1 &= 3; 112 | REQUIRE(accessor1 == 3); 113 | } 114 | 115 | TEST_CASE("Accessor, int, binary assignment operator |=") 116 | { 117 | accessorpp::Accessor accessor1; 118 | accessorpp::Accessor accessor2; 119 | 120 | accessor1 = 10; // 0b1010 121 | accessor2 = 6; // 0b0110 122 | 123 | accessor1 |= accessor2; 124 | REQUIRE(accessor1 == 14); 125 | REQUIRE(accessor2 == 6); 126 | 127 | accessor1 |= 1; 128 | REQUIRE(accessor1 == 15); 129 | } 130 | 131 | TEST_CASE("Accessor, int, binary assignment operator ^=") 132 | { 133 | accessorpp::Accessor accessor1; 134 | accessorpp::Accessor accessor2; 135 | 136 | accessor1 = 10; // 0b1010 137 | accessor2 = 6; // 0b0110 138 | 139 | accessor1 ^= accessor2; 140 | REQUIRE(accessor1 == 12); 141 | REQUIRE(accessor2 == 6); 142 | 143 | accessor1 ^= 6; 144 | REQUIRE(accessor1 == 10); 145 | } 146 | 147 | TEST_CASE("Accessor, int, binary assignment operator <<=") 148 | { 149 | accessorpp::Accessor accessor1; 150 | accessorpp::Accessor accessor2; 151 | 152 | accessor1 = 14; // 0b1110 153 | accessor2 = 2; 154 | 155 | accessor1 <<= accessor2; 156 | REQUIRE(accessor1 == 56); 157 | REQUIRE(accessor2 == 2); 158 | 159 | accessor1 <<= 1; 160 | REQUIRE(accessor1 == 112); 161 | } 162 | 163 | TEST_CASE("Accessor, int, binary assignment operator >>=") 164 | { 165 | accessorpp::Accessor accessor1; 166 | accessorpp::Accessor accessor2; 167 | 168 | accessor1 = 14; // 0b1110 169 | accessor2 = 2; 170 | 171 | accessor1 >>= accessor2; 172 | REQUIRE(accessor1 == 3); 173 | REQUIRE(accessor2 == 2); 174 | 175 | accessor1 >>= 1; 176 | REQUIRE(accessor1 == 1); 177 | } 178 | 179 | 180 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_operator_logic.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | TEST_CASE("Accessor, logic operator ==, !=, >, >=, <, <=") 18 | { 19 | accessorpp::Accessor accessor1; 20 | 21 | REQUIRE(accessor1 == 0); 22 | REQUIRE(accessor1 != 5); 23 | REQUIRE(accessor1 < 5); 24 | REQUIRE(accessor1 <= 0); 25 | REQUIRE(accessor1 > -1); 26 | REQUIRE(accessor1 >= 0); 27 | 28 | accessor1 = 5; 29 | REQUIRE(accessor1 == 5); 30 | REQUIRE(accessor1 != 0); 31 | 32 | accessorpp::Accessor accessor2; 33 | REQUIRE(! (accessor1 == accessor2)); 34 | REQUIRE(accessor1 != accessor2); 35 | REQUIRE(accessor1 > accessor2); 36 | REQUIRE(accessor1 >= accessor2); 37 | REQUIRE(accessor2 < accessor1); 38 | REQUIRE(accessor2 <= accessor1); 39 | accessor2 = 5; 40 | REQUIRE(accessor1 == accessor2); 41 | } 42 | 43 | TEST_CASE("Accessor, logic operator &&, ||") 44 | { 45 | accessorpp::Accessor accessor1; 46 | 47 | // The extra brackets () are to make CATCH happy, not required by Accessor. 48 | REQUIRE((accessor1 || 1)); 49 | 50 | accessor1 = 5; 51 | REQUIRE((accessor1 && 1)); 52 | 53 | accessorpp::Accessor accessor2; 54 | REQUIRE(accessor2 == false); 55 | accessor2 = true; 56 | const bool value = accessor1 || accessor2; 57 | REQUIRE(value); 58 | REQUIRE((accessor1 && accessor2)); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /tests/unittest/test_accessor_operator_unary.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/accessor.h" 16 | 17 | TEST_CASE("Accessor, int, unary operator ++") 18 | { 19 | accessorpp::Accessor accessor1; 20 | accessorpp::Accessor accessor2; 21 | 22 | accessor1 = 8; 23 | accessor2 = ++accessor1; 24 | REQUIRE(accessor1 == 9); 25 | REQUIRE(accessor2 == 9); 26 | } 27 | 28 | TEST_CASE("Accessor, int, unary operator --") 29 | { 30 | accessorpp::Accessor accessor1; 31 | accessorpp::Accessor accessor2; 32 | 33 | accessor1 = 8; 34 | accessor2 = accessor1++; 35 | REQUIRE(accessor1 == 9); 36 | REQUIRE(accessor2 == 8); 37 | } 38 | 39 | TEST_CASE("Accessor, int, unary operator !") 40 | { 41 | accessorpp::Accessor accessor1; 42 | accessorpp::Accessor accessor2; 43 | 44 | accessor1 = true; 45 | accessor2 = ! accessor1; 46 | REQUIRE(accessor1); 47 | REQUIRE(! accessor2); 48 | 49 | accessor2 = ! accessor2; 50 | REQUIRE(accessor1); 51 | REQUIRE(accessor2); 52 | } 53 | 54 | TEST_CASE("Accessor, int, unary operator +") 55 | { 56 | accessorpp::Accessor accessor1; 57 | accessorpp::Accessor accessor2; 58 | 59 | accessor1 = -5; 60 | accessor2 = +accessor1; 61 | REQUIRE(accessor1 == -5); 62 | REQUIRE(accessor2 == -5); 63 | 64 | accessor2 = -8; 65 | accessor2 = +accessor2; 66 | REQUIRE(accessor1 == -5); 67 | REQUIRE(accessor2 == -8); 68 | } 69 | 70 | TEST_CASE("Accessor, int, unary operator -") 71 | { 72 | accessorpp::Accessor accessor1; 73 | accessorpp::Accessor accessor2; 74 | 75 | accessor1 = 5; 76 | accessor2 = -accessor1; 77 | REQUIRE(accessor1 == 5); 78 | REQUIRE(accessor2 == -5); 79 | 80 | accessor2 = 8; 81 | accessor2 = -accessor2; 82 | REQUIRE(accessor1 == 5); 83 | REQUIRE(accessor2 == -8); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /tests/unittest/test_getter.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/getter.h" 16 | 17 | #include 18 | 19 | TEST_CASE("Getter, ctor") 20 | { 21 | int value = 5; 22 | accessorpp::Getter getter1(&value); 23 | REQUIRE(getter1.get() == 5); 24 | 25 | accessorpp::Getter getter2(getter1); 26 | REQUIRE(getter2.get() == 5); 27 | } 28 | 29 | TEST_CASE("Getter, int, variable") 30 | { 31 | int value = 5; 32 | accessorpp::Getter getter(&value); 33 | REQUIRE(getter.get() == 5); 34 | value = 8; 35 | REQUIRE(getter.get() == 8); 36 | } 37 | 38 | TEST_CASE("Getter, const int &, variable") 39 | { 40 | int value = 3; 41 | const int & ref = value; 42 | accessorpp::Getter getter(&ref); 43 | REQUIRE(getter.get() == 3); 44 | value = 6; 45 | REQUIRE(getter.get() == 6); 46 | } 47 | 48 | TEST_CASE("Getter, int &, variable") 49 | { 50 | int value = 38; 51 | int & ref = value; 52 | accessorpp::Getter getter(&ref); 53 | REQUIRE(getter.get() == 38); 54 | value = 98; 55 | REQUIRE(getter.get() == 98); 56 | } 57 | 58 | struct MyValue 59 | { 60 | explicit MyValue(const int value = 0) 61 | : value(value) { 62 | } 63 | 64 | int getValue() const { 65 | return value; 66 | } 67 | 68 | void set(const int newValue) const { 69 | const_cast(this)->value = newValue; 70 | } 71 | 72 | const int & getCref() const { 73 | return value; 74 | } 75 | 76 | int & getRef() { 77 | return value; 78 | } 79 | 80 | operator int() const { 81 | return value; 82 | } 83 | 84 | int value; 85 | }; 86 | 87 | TEST_CASE("Getter, int, member") 88 | { 89 | MyValue myValue(9); 90 | 91 | SECTION("Embed instance") 92 | { 93 | accessorpp::Getter getter(&MyValue::value, &myValue); 94 | REQUIRE(getter.get() == 9); 95 | myValue.value = 8; 96 | REQUIRE(getter.get() == 8); 97 | } 98 | 99 | SECTION("Pass instance") 100 | { 101 | accessorpp::Getter getter(&MyValue::value); 102 | REQUIRE(getter.get(&myValue) == 9); 103 | myValue.value = 8; 104 | REQUIRE(getter.get(&myValue) == 8); 105 | } 106 | } 107 | 108 | TEST_CASE("Getter, const int &, member") 109 | { 110 | MyValue myValue(9); 111 | accessorpp::Getter getter(&MyValue::value, &myValue); 112 | REQUIRE(getter.get() == 9); 113 | myValue.value = 8; 114 | REQUIRE(getter.get() == 8); 115 | } 116 | 117 | TEST_CASE("Getter, int &, member") 118 | { 119 | MyValue myValue(9); 120 | accessorpp::Getter getter(&MyValue::value, &myValue); 121 | REQUIRE(getter.get() == 9); 122 | myValue.value = 8; 123 | REQUIRE(getter.get() == 8); 124 | } 125 | 126 | TEST_CASE("Getter, int, member getValue()") 127 | { 128 | MyValue myValue(9); 129 | 130 | SECTION("Embed instance") 131 | { 132 | accessorpp::Getter getter(&MyValue::getValue, &myValue); 133 | REQUIRE(getter.get() == 9); 134 | myValue.value = 8; 135 | REQUIRE(getter.get() == 8); 136 | } 137 | 138 | SECTION("Pass instance") 139 | { 140 | accessorpp::Getter getter(&MyValue::getValue); 141 | REQUIRE(getter.get(&myValue) == 9); 142 | myValue.value = 8; 143 | REQUIRE(getter.get(&myValue) == 8); 144 | } 145 | } 146 | 147 | TEST_CASE("Getter, int, member getCref()") 148 | { 149 | MyValue myValue(9); 150 | accessorpp::Getter getter(&MyValue::getCref, &myValue); 151 | REQUIRE(getter.get() == 9); 152 | myValue.value = 8; 153 | REQUIRE(getter.get() == 8); 154 | } 155 | 156 | TEST_CASE("Getter, const int &, member getCref()") 157 | { 158 | { 159 | MyValue myValue(9); 160 | accessorpp::Getter getter(&MyValue::getCref, &myValue); 161 | REQUIRE(getter.get() == 9); 162 | myValue.value = 8; 163 | REQUIRE(getter.get() == 8); 164 | } 165 | { 166 | const MyValue myValue(9); 167 | accessorpp::Getter getter(&MyValue::getCref, &myValue); 168 | REQUIRE(getter.get() == 9); 169 | myValue.set(8); 170 | REQUIRE(getter.get() == 8); 171 | } 172 | } 173 | 174 | TEST_CASE("Getter, int, member getRef()") 175 | { 176 | MyValue myValue(9); 177 | accessorpp::Getter getter(&MyValue::getRef, &myValue); 178 | REQUIRE(getter.get() == 9); 179 | myValue.value = 8; 180 | REQUIRE(getter.get() == 8); 181 | } 182 | 183 | TEST_CASE("Getter, int, MyValue variable") 184 | { 185 | MyValue myValue(9); 186 | accessorpp::Getter getter(&myValue); 187 | REQUIRE(getter.get().getValue() == 9); 188 | myValue.value = 8; 189 | REQUIRE(getter.get().getValue() == 8); 190 | myValue.value = 3; 191 | const int converted = (MyValue)getter; 192 | REQUIRE(converted == 3); 193 | } 194 | 195 | TEST_CASE("Getter, ostream") 196 | { 197 | int value = 5; 198 | accessorpp::Getter getter(&value); 199 | std::stringstream stream1; 200 | std::stringstream stream2; 201 | stream1 << getter; 202 | REQUIRE(stream1.str() != stream2.str()); 203 | stream2 << 5; 204 | REQUIRE(stream1.str() == stream2.str()); 205 | } 206 | -------------------------------------------------------------------------------- /tests/unittest/test_setter.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include "test.h" 15 | #include "accessorpp/setter.h" 16 | 17 | #include 18 | 19 | TEST_CASE("Setter, ctor") 20 | { 21 | int value = 0; 22 | accessorpp::Setter setter1(&value); 23 | setter1 = 8; 24 | REQUIRE(value == 8); 25 | 26 | accessorpp::Setter setter2(setter1); 27 | setter2 = 5; 28 | REQUIRE(value == 5); 29 | } 30 | 31 | TEST_CASE("Setter, int, variable") 32 | { 33 | int value = 0; 34 | accessorpp::Setter setter(&value); 35 | REQUIRE(value == 0); 36 | setter = 8; 37 | REQUIRE(value == 8); 38 | } 39 | 40 | TEST_CASE("Setter, const int &, variable") 41 | { 42 | int value = 0; 43 | accessorpp::Setter setter(&value); 44 | REQUIRE(value == 0); 45 | setter = 8; 46 | REQUIRE(value == 8); 47 | } 48 | 49 | TEST_CASE("Setter, int &, variable") 50 | { 51 | int value = 0; 52 | accessorpp::Setter setter(&value); 53 | REQUIRE(value == 0); 54 | int assign = 8; 55 | setter = assign; 56 | REQUIRE(value == 8); 57 | } 58 | 59 | struct MyValue 60 | { 61 | MyValue(const int value = 0) 62 | : value(value) { 63 | } 64 | 65 | int getValue() const { 66 | return value; 67 | } 68 | 69 | void setValue(const int newValue) { 70 | value = newValue; 71 | } 72 | 73 | void setConst(const int newValue) const { 74 | const_cast(this)->value = newValue; 75 | } 76 | 77 | int value; 78 | }; 79 | 80 | TEST_CASE("Setter, int, member") 81 | { 82 | MyValue myValue(0); 83 | REQUIRE(myValue.getValue() == 0); 84 | 85 | SECTION("Embed instance") 86 | { 87 | accessorpp::Setter setter(&MyValue::value, &myValue); 88 | setter = 8; 89 | REQUIRE(myValue.getValue() == 8); 90 | } 91 | 92 | SECTION("Pass instance") 93 | { 94 | accessorpp::Setter setter(&MyValue::value); 95 | setter.set(8, &myValue); 96 | REQUIRE(myValue.getValue() == 8); 97 | } 98 | } 99 | 100 | TEST_CASE("Setter, const int &, member") 101 | { 102 | MyValue myValue(0); 103 | REQUIRE(myValue.getValue() == 0); 104 | accessorpp::Setter setter(&MyValue::value, &myValue); 105 | setter = 8; 106 | REQUIRE(myValue.getValue() == 8); 107 | const int assign = 38; 108 | setter = assign; 109 | REQUIRE(myValue.getValue() == 38); 110 | } 111 | 112 | TEST_CASE("Setter, int &, member") 113 | { 114 | MyValue myValue(0); 115 | REQUIRE(myValue.getValue() == 0); 116 | accessorpp::Setter setter(&MyValue::value, &myValue); 117 | int assign = 8; 118 | setter = assign; 119 | REQUIRE(myValue.getValue() == 8); 120 | } 121 | 122 | TEST_CASE("Setter, int, member setValue") 123 | { 124 | MyValue myValue(0); 125 | REQUIRE(myValue.getValue() == 0); 126 | 127 | SECTION("Embed instance") 128 | { 129 | accessorpp::Setter setter(&MyValue::setValue, &myValue); 130 | setter = 8; 131 | REQUIRE(myValue.getValue() == 8); 132 | } 133 | 134 | SECTION("Pass instance") 135 | { 136 | accessorpp::Setter setter(&MyValue::setValue); 137 | setter.set(8, &myValue); 138 | REQUIRE(myValue.getValue() == 8); 139 | } 140 | } 141 | 142 | TEST_CASE("Setter, const int &, member setValue") 143 | { 144 | MyValue myValue(0); 145 | REQUIRE(myValue.getValue() == 0); 146 | accessorpp::Setter setter(&MyValue::setValue, &myValue); 147 | setter = 8; 148 | REQUIRE(myValue.getValue() == 8); 149 | } 150 | 151 | TEST_CASE("Setter, int &, member setValue") 152 | { 153 | MyValue myValue(0); 154 | REQUIRE(myValue.getValue() == 0); 155 | accessorpp::Setter setter(&MyValue::setValue, &myValue); 156 | int assign = 8; 157 | setter = assign; 158 | REQUIRE(myValue.getValue() == 8); 159 | } 160 | 161 | TEST_CASE("Setter, int, member setConst") 162 | { 163 | MyValue myValue(0); 164 | REQUIRE(myValue.getValue() == 0); 165 | accessorpp::Setter setter(&MyValue::setConst, &myValue); 166 | setter = 8; 167 | REQUIRE(myValue.getValue() == 8); 168 | } 169 | 170 | TEST_CASE("Setter, const int &, member setConst") 171 | { 172 | MyValue myValue(0); 173 | REQUIRE(myValue.getValue() == 0); 174 | accessorpp::Setter setter(&MyValue::setConst, &myValue); 175 | setter = 8; 176 | REQUIRE(myValue.getValue() == 8); 177 | } 178 | 179 | TEST_CASE("Setter, int &, member setConst") 180 | { 181 | MyValue myValue(0); 182 | REQUIRE(myValue.getValue() == 0); 183 | accessorpp::Setter setter(&MyValue::setConst, &myValue); 184 | int assign = 8; 185 | setter = assign; 186 | REQUIRE(myValue.getValue() == 8); 187 | } 188 | 189 | TEST_CASE("Setter, istream") 190 | { 191 | int value = 5; 192 | accessorpp::Setter setter(&value); 193 | std::stringstream stream; 194 | REQUIRE(value == 5); 195 | stream.str("38"); 196 | stream >> setter; 197 | REQUIRE(value == 38); 198 | } 199 | -------------------------------------------------------------------------------- /tests/unittest/testmain.cpp: -------------------------------------------------------------------------------- 1 | // accessorpp library 2 | // Copyright (C) 2022 Wang Qi (wqking) 3 | // Github: https://github.com/wqking/accessorpp 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 15 | #include "test.h" 16 | -------------------------------------------------------------------------------- /tools/generateops.py: -------------------------------------------------------------------------------- 1 | logicOperatorList = [ 2 | '==', '!=', '>', '>=', '<', '<=', 3 | '&&', '||', 4 | ] 5 | logicOperatorTemplate = ''' 6 | template 7 | auto operator {op} (const T & a, const U & b) 8 | -> typename std::enable_if::value, bool>::type 9 | { 10 | return (typename AccessorValueType::Type)(a) {op} (typename AccessorValueType::Type)(b); 11 | } 12 | ''' 13 | 14 | binaryOperatorList = [ 15 | '+', '-', '*', '/', '%', 16 | '&', '|', '^', '<<', '>>', 17 | ] 18 | 19 | binaryOperatorTemplate = ''' 20 | template 21 | auto operator {op} (const T & a, const U & b) 22 | -> typename std::enable_if::value && T::internalStorage, T>::type 23 | { 24 | T result(a); 25 | result = (typename AccessorValueType::Type)(a) {op} (typename AccessorValueType::Type)(b); 26 | return result; 27 | } 28 | ''' 29 | 30 | binaryAssignOperatorList = [ 31 | '+=', '-=', '*=', '/=', '%=', 32 | '&=', '|=', '^=', '<<=', '>>=', 33 | ] 34 | 35 | binaryAssignOperatorTemplate = ''' 36 | template 37 | auto operator {op} (T & a, const U & b) 38 | -> typename std::enable_if::value, T &>::type 39 | { 40 | a = (typename AccessorValueType::Type)(a) {rop} (typename AccessorValueType::Type)(b); 41 | return a; 42 | } 43 | ''' 44 | 45 | def doGenerate(operatorList, operatorTemplate) : 46 | for operator in operatorList : 47 | rop = operator.replace('=', '') 48 | code = operatorTemplate; 49 | code = code.replace('{op}', operator) 50 | code = code.replace('{rop}', rop) 51 | print(code, end = '') 52 | 53 | print('// Logic operators') 54 | doGenerate(logicOperatorList, logicOperatorTemplate) 55 | print('// Binary operators') 56 | doGenerate(binaryOperatorList, binaryOperatorTemplate) 57 | print('// Binary assignment operators') 58 | doGenerate(binaryAssignOperatorList, binaryAssignOperatorTemplate) 59 | --------------------------------------------------------------------------------