├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── doc ├── BEGIN_SPECIALIZATION_END_SPECIALIZATION.qbk ├── Jamfile ├── MM_CLASS.qbk ├── MM_CLASS_MULTI.qbk ├── MM_FOREIGN_CLASS.qbk ├── MM_INIT.qbk ├── MULTI_METHOD_MACRO.qbk ├── advanced_topics.qbk ├── ambiguous.qbk ├── initialize.qbk ├── installation.qbk ├── intro.qbk ├── multi_method.qbk ├── multi_method_operator_call.qbk ├── multi_method_specialize.qbk ├── multi_methods.qbk ├── next.cpp ├── next.qbk ├── reference.qbk ├── reference_examples.cpp ├── selector.qbk ├── tutorial.qbk ├── undefined.qbk ├── upload └── virtual_.qbk ├── examples ├── CMakeLists.txt ├── asteroids.cpp ├── dl.hpp ├── dl_main.cpp ├── dl_shared.cpp ├── foreign.cpp ├── matrix.cpp ├── next.cpp └── three.cpp ├── go ├── include └── yorel │ ├── methods.hpp │ ├── methods │ ├── detail.hpp │ ├── extern_macros.hpp │ ├── macros.hpp │ ├── no_macros.hpp │ └── runtime.hpp │ ├── multi_methods.hpp │ └── multi_methods │ ├── extern_macros.hpp │ └── macros.hpp ├── src ├── CMakeLists.txt └── yomm11.cpp └── tests ├── CMakeLists.txt ├── adjust.hpp ├── animals.hpp ├── benchmarks.cpp ├── benchmarks.hpp ├── benchmarks_fast.cpp ├── mi.hpp ├── order.hpp ├── order1.cpp ├── order2.cpp ├── run ├── tests.cpp └── util ├── animals_mi.hpp └── join.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | *.s 3 | *.o 4 | *.d 5 | *.a 6 | /tests/experiments 7 | /tests/tests 8 | /examples/three 9 | /examples/asteroids 10 | /examples/next 11 | /examples/foreign 12 | /tests/order1 13 | /tests/order12 14 | /tests/order2 15 | /tests/order21 16 | /tests/benchmarks 17 | /doc/html 18 | /doc/next 19 | /doc/images 20 | /doc/examples 21 | /doc/reference.xml 22 | /examples/dl_main 23 | /examples/dl_shared.so 24 | /examples/examples 25 | CMakeCache.txt 26 | CMakeFiles/ 27 | Testing/ 28 | *.cmake 29 | Makefile 30 | /examples/libdl_shared.so 31 | /install_manifest.txt 32 | /doc/multi_methods.xml 33 | /build 34 | *.sdf 35 | *.suo 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # -*- compile-command: "make && make test" -*- 2 | 3 | # CMakeLists.txt 4 | # Copyright (c) 2013 Jean-Louis Leroy 5 | # Distributed under the Boost Software License, Version 1.0. (See 6 | # accompanying file LICENSE_1_0.txt or copy at 7 | # http:#www.boost.org/LICENSE_1_0.txt) 8 | 9 | # for clang: cmake -DCMAKE_CXX_COMPILER=clang++ CMakeLists.txt 10 | 11 | cmake_minimum_required (VERSION 2.6) 12 | 13 | project (YOMM11) 14 | 15 | set (YOMM11_ERSION_MAJOR 1) 16 | set (YOMM11_VERSION_MINOR 0) 17 | 18 | include_directories ("${YOMM11_SOURCE_DIR}/include") 19 | link_directories ("${YOMM11_BINARY_DIR}/src") 20 | 21 | if(CMAKE_COMPILER_IS_GNUCXX) 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 23 | endif() 24 | 25 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 27 | endif() 28 | 29 | if(MSVC) 30 | set(CMAKE_CXX_FLAGS "-D_SCL_SECURE_NO_WARNINGS /EHsc") 31 | endif() 32 | 33 | add_subdirectory (src) 34 | add_subdirectory (tests) 35 | add_subdirectory (examples) 36 | 37 | enable_testing() 38 | 39 | add_test (tests tests/tests) 40 | add_test (order12 tests/order12) 41 | add_test (order21 tests/order21) 42 | add_test (asteroids examples/asteroids) 43 | add_test (next examples/next) 44 | add_test (foreign examples/foreign) 45 | add_test (three examples/three) 46 | #add_test (NAME shared WORKING_DIRECTORY examples COMMAND dl_main) 47 | 48 | INSTALL (DIRECTORY include/yorel DESTINATION include) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YOMM11 IS DEPRECATED 2 | 3 | Thank you for the stars and the feedback! 4 | 5 | In 2017 I learned the D language, and I 6 | implemented 7 | [an open method library for D](https://github.com/jll63/openmethods.d). In the 8 | process I had a few fresh ideas, some of which are applicable to C++. Also, my 9 | colleague David Goffredo educated me on the power of modern preprocessor 10 | macros. 11 | 12 | Consequently, I set out to re-implement Yomm11 from scratch. The result 13 | is [Yomm2](https://github.com/jll63/yomm2), which I feel is a much better 14 | library. Read about the 15 | improvements 16 | [here](https://github.com/jll63/yomm2/blob/master/yomm11-yomm2.md). 17 | 18 | I will no longer actively develop Yomm11 but I will consider PRs. I strongly 19 | recommend switching to Yomm2 though. 20 | 21 | ## Old content: 22 | 23 | This library implements open multi-methods for C++11. 24 | 25 | Salient features are: 26 | 27 | * syntax: is relatively uncluttered. There are no limitations on the 28 | number of virtual arguments. Virtual and non-virtual arguments can 29 | be arbitrarily mixed. Virtual and multiple inheritance are 30 | supported. 31 | 32 | * speed: close to a virtual function call when the hierarchies 33 | involved in the virtual arguments collaborate with the 34 | library. Calling a method that does nothing, with a single virtual 35 | argument in a single inheritance hierarchy is 33% slower than the 36 | equivalent virtual function call. The difference becomes 37 | unnoticeable if the functions perform a few simple maths 38 | operations. See tests/benchmarks.cpp. 39 | 40 | * size: dispatch tables are constructed in terms of class groups. This 41 | results in a tables devoid of redundancies. 42 | 43 | * support for "foreign" class hierarchies: the library can be used 44 | without modifications to existing classes, at the cost of lower 45 | performance. Collaborating and foreign arguments can be freely 46 | mixed. Performance is still quite good, see the benchmarks. 47 | 48 | * next: a pointer to the next most specialized method is available 49 | inside method specializations - see examples/next.cpp. 50 | Alternatively, it is possible to call a specialization directly. 51 | 52 | Documentation: http://www.yorel.be/mm/ - see also the articles on Code 53 | Project http://tinyurl.com/m8kg2y3 54 | 55 | Support and discussions: yomm11 on Google Groups 56 | (https://groups.google.com/forum/#!forum/yomm11) 57 | 58 | Author: Jean-Louis Leroy - jl@yorel.be 59 | -------------------------------------------------------------------------------- /doc/BEGIN_SPECIALIZATION_END_SPECIALIZATION.qbk: -------------------------------------------------------------------------------- 1 | [/ -*- compile-command: "bjam" -*-; auto-fill-mode: t /] 2 | 3 | [section BEGIN_SPECIALIZATION / END_SPECIALIZATION ] 4 | 5 | [h3 Synopsis] 6 | 7 | BEGIN_SPECIALIZATION(__name__, __return_type__, __args__...) { 8 | [~body] 9 | } END_SPECIALIZATION; 10 | 11 | [h3 Description] 12 | 13 | Specializes multi-method __name__. The code between braces is the 14 | specialization's implementation. 15 | 16 | __name__ must be the name of a previously declared 17 | multi-method. __return_type__ must be conbertible to the multi-method's 18 | return type. __args__ must have the same length as the multi-method's 19 | argument list. Each argument must be either the same as the 20 | multi-method's argument in the same position, or if the multi-method 21 | has marked the argument with __virtual___, a reference to an object of a 22 | compliant type. 23 | 24 | [h3 Examples] 25 | 26 | Three specializations for `MULTI_METHOD(approve, bool, const virtual_&, const virtual_&, const virtual_&)`: 27 | 28 | `` 29 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const role&, const reason&) { 30 | return false; 31 | } END_SPECIALIZATION; 32 | 33 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const ceo&, const reason&) { 34 | return true; 35 | } END_SPECIALIZATION; 36 | 37 | BEGIN_SPECIALIZATION(approve, bool, const cab&, const manager&, const business&) { 38 | return true; 39 | } END_SPECIALIZATION; 40 | `` 41 | 42 | Two specializations for `MULTI_METHOD(collide, void, virtual_&, virtual_&, bool swapped)`: 43 | 44 | `` 45 | BEGIN_SPECIALIZATION(collide, void, object& a, object& b, bool swapped) { 46 | if (swapped) { 47 | // kaboom 48 | } else { 49 | return collide(b, a, true); 50 | } 51 | } END_SPECIALIZATION; 52 | 53 | BEGIN_SPECIALIZATION(collide, void, asteroid& a, asteroid& b, bool swapped) { 54 | // do nothing 55 | } END_SPECIALIZATION; 56 | `` 57 | 58 | [endsect] 59 | -------------------------------------------------------------------------------- /doc/Jamfile: -------------------------------------------------------------------------------- 1 | # -*- compile-command: "bjam" -*- 2 | 3 | project multi-methods/doc ; 4 | import boostbook : boostbook ; 5 | using quickbook ; 6 | using doxygen ; 7 | 8 | path-constant images_location : html ; 9 | 10 | boostbook quickbook 11 | : 12 | multi_methods.qbk 13 | : 14 | chunk.first.sections=1 15 | chunk.section.depth=4 16 | toc.section.depth=3 17 | toc.max.depth=3 18 | #nav.layout=none 19 | boost.defaults=none 20 | ; 21 | -------------------------------------------------------------------------------- /doc/MM_CLASS.qbk: -------------------------------------------------------------------------------- 1 | [section MM_CLASS] 2 | 3 | [h3 Synopsis] 4 | 5 | MM_CLASS(__class__, __bases__...) 6 | 7 | [h3 Description] 8 | 9 | Registers a class for efficient multi-method dispatch. 10 | 11 | Macro `MM_CLASS` must be called inside the definition of __class__. The 12 | subsequent arguments __bases__ consist in the (possibly empty) list 13 | of base classes that the dispatcher must take into account. __class__ 14 | must derive (directly or indirectly) from __selector__. 15 | 16 | [h3 Example] 17 | `` 18 | struct animal : yorel::methods::selector { 19 | MM_CLASS(animal); 20 | }; 21 | 22 | struct female : virtual animal { 23 | MM_CLASS(female, animal); 24 | }; 25 | 26 | struct horse : virtual animal { 27 | MM_CLASS(horse, animal); 28 | }; 29 | 30 | struct mare : horse, female { 31 | MM_CLASS(mare, horse, female); 32 | }; 33 | `` 34 | 35 | [h3 Implementation notes] 36 | 37 | `MM_CLASS` expands to: 38 | 39 | `` 40 | virtual void _mm_init_class_() { 41 | &::yorel::methods::mm_class_initializer>::the; 42 | } 43 | `` 44 | 45 | This forces the instantiation of a class template and one static object that fills an internal data structure with inheritance information. 46 | 47 | [endsect] 48 | -------------------------------------------------------------------------------- /doc/MM_CLASS_MULTI.qbk: -------------------------------------------------------------------------------- 1 | [section MM_CLASS_MULTI] 2 | 3 | [h3 Synopsis] 4 | 5 | MM_CLASS_MULTI(__class__, __bases__...) 6 | 7 | [h3 Description] 8 | 9 | Same as [link methods.reference.registering_classes.mm_class MM_CLASS], to be used 10 | in presence of *repeated* inheritance of class __selector__. 11 | 12 | `MM_CLASS_MULTI` should not be used if __class__ inherits (virtually) 13 | the same __selector__ sub-object via different inheritance paths. 14 | 15 | [h3 Examples] 16 | 17 | `` 18 | 19 | struct Satellite : selector { 20 | MM_CLASS(Satellite); 21 | }; 22 | 23 | struct Displayable : selector { 24 | MM_CLASS(Displayable); 25 | }; 26 | 27 | struct DisplayableSatellite { 28 | MM_CLASS_MULTI(DisplayableSatellite, Satellite, Displayable); 29 | }; 30 | 31 | `` 32 | 33 | [h3 Implementation notes] 34 | 35 | `MM_CLASS_MULTI` expands to: 36 | 37 | `` 38 | MM_CLASS(CLASS, BASE, BASES); \ 39 | std::vector* _get_mm_ptbl() const { return BASE::_mm_ptbl; } 40 | `` 41 | 42 | `_get_mm_ptbl` returns a pointer to a vector of offsets into 43 | multi-method dispatch tables, contained in inherited class 44 | __selector__. This is exactly what the same function in __selector__ does, 45 | and returning the table from one of the __selector__ sub-objects or the 46 | other does not matter. Re-implementing it in the class merely lifts 47 | the ambiguity that would otherwise be reported during compilation. 48 | 49 | [endsect] 50 | -------------------------------------------------------------------------------- /doc/MM_FOREIGN_CLASS.qbk: -------------------------------------------------------------------------------- 1 | [section MM_FOREIGN_CLASS] 2 | 3 | [h3 Synopsis] 4 | 5 | MM_FOREIGN_CLASS(__class__, __bases__...) 6 | 7 | [h3 Description] 8 | 9 | Registers a class for multi-method dispatch, non-intrusively. 10 | 11 | Macro `MM_FOREIGN_CLASS` must be called outside the definition of 12 | __class__, in an implementation file. __class__ must be polymorphic, 13 | i.e. have at least one virtual function. The subsequent arguments __bases__ 14 | consist in the (possibly empty) list of base classes that the 15 | dispatcher must take into account. 16 | 17 | [h3 Examples] 18 | `` 19 | struct animal { 20 | virtual ~animal(); 21 | }; 22 | 23 | struct female : virtual animal { 24 | }; 25 | 26 | struct horse : virtual animal { 27 | }; 28 | 29 | struct mare : horse, female { 30 | }; 31 | 32 | // in .cpp file(s) 33 | MM_FOREIGN_CLASS(animal); 34 | MM_FOREIGN_CLASS(female, animal); 35 | MM_FOREIGN_CLASS(horse, animal); 36 | MM_FOREIGN_CLASS(mare, horse, female); 37 | `` 38 | [endsect] 39 | -------------------------------------------------------------------------------- /doc/MM_INIT.qbk: -------------------------------------------------------------------------------- 1 | [section MM_INIT] 2 | 3 | [h3 Synopsis] 4 | 5 | `` 6 | // in constructor 7 | MM_INIT(); 8 | `` 9 | 10 | [h3 Description] 11 | 12 | Initialize the pointer to the multi-method offset table. 13 | 14 | `MM_INIT()` must be called in each constructor of each class registered for fast dispatch. Even if a class does not otherwise need constructors (e.g. the generated default and copy constructors could suffice), they must be provided for the benefit of calling `MM_INIT()`. 15 | 16 | [h3 Example] 17 | 18 | `` 19 | 20 | matrix::matrix() { 21 | MM_INIT(); 22 | // ... 23 | } 24 | 25 | matrix::matrix(const matrix& other) { 26 | MM_INIT(); 27 | // ... 28 | } 29 | 30 | matrix::matrix(int rows, int cols) { 31 | MM_INIT(); 32 | // ... 33 | } 34 | 35 | `` 36 | 37 | [h3 Implementation notes] 38 | 39 | `MM_INIT` expands to: 40 | 41 | `` 42 | this->_init_mmptr(this) 43 | `` 44 | 45 | `_init_mmptr` is a templatized member function that infers the current 46 | class, finds the internal data structure that describes it and 47 | contains a vector of offsets into multi-method dispatch tables. It 48 | sets the pointer contained in inherited class __selector__ to that table. 49 | 50 | [endsect] 51 | -------------------------------------------------------------------------------- /doc/MULTI_METHOD_MACRO.qbk: -------------------------------------------------------------------------------- 1 | [section MULTI_METHOD] 2 | 3 | [h3 Synopsis] 4 | MULTI_METHOD(__name__, __return_type__, __args__...) 5 | 6 | [h3 Description] 7 | 8 | Declares a multi-method that takes __args__ as an argument list and 9 | returns a __return_type__. 10 | 11 | __args__ is an argument list, just like what is provided to an ordinary 12 | function declaration. However, arguments marked with the __virtual___ 13 | modifier are considered in the selection of the actual code to be 14 | executed given the dynamic value passed in these positons. 15 | 16 | Virtual arguments must have their type registered with the 17 | MM_CLASS family of macros. Virtual arguments, either of the intrusive 18 | or foreign kinds, and ordinary function arguments can be freely 19 | intermixed. 20 | 21 | Multi-method declarations can appear in header files - __name__ is 22 | defined as a `constexpr` function object. 23 | 24 | __name__ must be unique in its scope. Overloading is not supported. 25 | 26 | [h3 Examples] 27 | 28 | A multimethod named `approve` that takes three virtual arguments and 29 | returns a boolean: 30 | 31 | `` 32 | MULTI_METHOD(approve, bool, const virtual_&, const virtual_&, const virtual_&); 33 | `` 34 | 35 | A `collide` multi-method that takes two virtual and one ordinary 36 | argument and returns nothing: 37 | `` 38 | MULTI_METHOD(collide, void, virtual_&, virtual_&, bool swapped); 39 | `` 40 | 41 | [endsect] 42 | -------------------------------------------------------------------------------- /doc/advanced_topics.qbk: -------------------------------------------------------------------------------- 1 | [/ -*- compile-command: "bjam" -*-; auto-fill-mode: t /] 2 | 3 | [section:advanced_topics Advanced topics] 4 | 5 | [section Virtual and multiple inheritance] 6 | 7 | 8 | All forms of virtual and multiple inheritance are supported, sometimes 9 | at a performance cost or some extra work on your part. 10 | 11 | [h3 Virtual inheritance] 12 | 13 | Virtual inheritance is not a problem, however, a call to a 14 | multi-method that involves an argument that is virtually inherited in 15 | the selected specialization will incur the cost of a 16 | `dynamic_cast`. For example: 17 | 18 | `` 19 | struct X { ... }; 20 | struct A : X { ... }; 21 | struct B : virtual X { ... }; 22 | 23 | MULTI_METHOD(foo, void, const virtual_, const virtual_); 24 | 25 | BEGIN_SPECIALIZATION(foo, void, const X&, const X&) 26 | // ... 27 | END_SPECIALIZATION; 28 | 29 | BEGIN_SPECIALIZATION(foo, void, const A&, const B&) 30 | // ... 31 | END_SPECIALIZATION; 32 | 33 | foo(X(), X()); // 1 34 | foo(A(), B()); // 2 35 | `` 36 | 37 | Here the first call to `foo()` does not necessitate any casts: both 38 | arguments are `X` objects. 39 | 40 | As for the second call, the first argument needs to be converted to a 41 | `A` before calling the specialization. That can be achieved with a 42 | `static_cast`, an operation that has a zero runtime cost. The second 43 | argument, however, requires a cast from virtual base class `X` to 44 | `B` - performed via a costly `dynamic_cast`. 45 | 46 | [h3 Multiple `selector` sub-objects] 47 | 48 | It may happen that class `selector` is repeatedly inherited, even in 49 | inheritance graphs that don't involve repeated inheritance by 50 | themselves. For example: 51 | 52 | `` 53 | struct X : selector { ... }; 54 | struct Y : selector { ... }; 55 | struct XY : X, Y { ... }; 56 | 57 | MULTI_METHOD(foo, void, const virtual_&); 58 | MULTI_METHOD(bar, void, const virtual_&); 59 | 60 | foo(XY()); // 1 61 | bar(XY()); // 2 62 | `` 63 | 64 | The first call poses no problem. `foo` takes a `X` object, which 65 | contains a single `selector` sub-object. 66 | 67 | The second call is more difficult: `bar` takes a `XY` object that 68 | contains /two/ `selector` sub-object. Truth is, it does not matter 69 | which `selector` it uses: they contain the same 70 | information. Nonetheless, you must tell C++ which `selector` to 71 | use. 72 | 73 | This is achieved by using a special form of the `MM_CLASS` macro: 74 | 75 | `` 76 | struct XY : X, Y { 77 | MM_CLASS_MULTI(XY, X, Y); 78 | 79 | XY() { 80 | MM_INIT(); 81 | } 82 | }; 83 | `` 84 | 85 | [h3 Repeated inheritance] 86 | 87 | Repeated inheritance is supported but unfortunately requires a 88 | cumbersome operation on the user's part. 89 | 90 | Consider: 91 | 92 | `` 93 | namespace foo { 94 | struct X : selector { ... }; 95 | struct A : X { ... }; 96 | struct B : X { ... }; 97 | struct AB : A, B { ... }; 98 | 99 | MULTI_METHOD(mx, int, const virtual_&); 100 | `` 101 | 102 | This is a case of multiple `selector` sub-objects, thus the 103 | recommendations from the previous section must be applied. But it does 104 | not suffice. Consider: 105 | 106 | `` 107 | // still in namespace foo 108 | 109 | BEGIN_SPECIALIZATION(mx, int, const AB& a) { 110 | ... 111 | } END_SPECIALIZATION; 112 | } 113 | `` 114 | Before calling the specialization, a cast from `X` to `AB` is 115 | required, and the address of `this` needs to be adjusted. However, the 116 | compiler cannot tell which `X` sub-object the cast is originating 117 | from. Or rather, `static_cast` cannot. The solution is to force a 118 | `dynamic_cast` instead. Sadly, this requires leaving the current 119 | namespace and moving to the `methods` namespace to introduce a 120 | specialization of class template `cast`: 121 | 122 | 123 | `` 124 | ... 125 | MULTI_METHOD(mx, int, const virtual_&); 126 | } // leave namespace foo 127 | 128 | namespace methods { 129 | template<> 130 | struct cast : 131 | cast_using_dynamic_cast { }; 132 | } 133 | 134 | namespace foo { // go back to namespace foo 135 | 136 | BEGIN_SPECIALIZATION(mx, int, const AB& a) { 137 | ... 138 | } END_SPECIALIZATION; 139 | } 140 | 141 | `` 142 | 143 | If you know of a better solution within C++11, please tell me about 144 | it. Future versions of C++ may feature the detection of a class' bases 145 | at compile-time. The appropriate cast could then be infered 146 | automatically, the same way as the library does in presence of virtual inheritance. 147 | 148 | That being said, I don't recall ever needing repeated inheritance 149 | since templates were introduced in C++. 150 | 151 | [endsect] 152 | 153 | [section No macros please] 154 | 155 | It is quite feasible to use the library without using the macros. They 156 | are convenient, but by no means necessary. See the reference, each 157 | macro contains a section showing the code it expands to and its 158 | rationale. 159 | 160 | Header `methods/no_macros.hpp` contains only one macro definition, 161 | used for debugging purpose. All the documented macros are defined in 162 | `methods/macros.hpp`. 163 | 164 | [endsect] 165 | 166 | [section:dynamic_linking Shared objects and dynamic loading] 167 | 168 | Class hierarchies and multi-method and their specializations can span 169 | executable and shared objects. Dynamic loading is supported but [*do 170 | not forget to call [link methods.reference.calling.initialize 171 | `initialize()`]] each time that classes or specializations are added 172 | or removed. 173 | 174 | The library relies heavily on instances of class templates to store 175 | information describing class hierarchies and multi-methods. In 176 | presence of shared objects, this can cause the same objects to be 177 | instantiated in several application or library objects. Normally this 178 | is not a problem, because the dynamic linker will discard duplicate 179 | instantiations just like the static linker does - or at least it works 180 | that way on my platform (Ubuntu GNU/Linux and g++). 181 | 182 | Should you want to avoid this, there is a solution: header 183 | `methods/extern_macros.hpp` replaces selected macros with 184 | versions that do not trigger template instantiation. If you want to 185 | use this mechanism, you should also put a call to `MM_EXTERN_CLASS` at 186 | file scope for each class participating in the fast dispatch scheme 187 | (i.e. containing either a `MM_CLASS` or a `MM_CLASS_MULTI`). 188 | 189 | Note that foreign classes do not incur the duplication problem because 190 | they are declared in implementation files. 191 | 192 | An example of dynamic loading that uses `extern_macros.hpp` is provided in 193 | the `examples` directory. See [@examples/dl.hpp dl.hpp], 194 | [@examples/dl_main.cpp dl_main.cpp] and [@examples/dl_shared.cpp dl_shared.cpp]. 195 | 196 | [endsect] 197 | 198 | [endsect] -------------------------------------------------------------------------------- /doc/ambiguous.qbk: -------------------------------------------------------------------------------- 1 | [section ambiguous] 2 | 3 | [h3 Synopsis] 4 | 5 | try { 6 | __mm__(__args__...); 7 | } catch (ambiguous&) { 8 | ... 9 | } 10 | 11 | [h3 Description] 12 | 13 | Thrown by __multi_method_operator_call__ when several specialization 14 | exist that take __args__ as arguments, but none of them is more 15 | specific than all the others. 16 | 17 | [h3 Example] 18 | 19 | See synopsis. 20 | 21 | [endsect] 22 | -------------------------------------------------------------------------------- /doc/initialize.qbk: -------------------------------------------------------------------------------- 1 | [section initialize] 2 | 3 | [h3 Synopsis] 4 | 5 | yorel::methods::initialize(); 6 | 7 | [h3 Description] 8 | 9 | Computes or re-computes the data structures underlying multi-method 10 | dispatch. 11 | 12 | `initialize` should be called after all the classes and 13 | multi-methods have been declared and before calling any multi-method, 14 | typically at the beginning of `main()`. If classes or multi-methods 15 | are added or removed (e.g. because of dynamic loading or un-loading of 16 | shared libraries), `intitialize` must be called again. 17 | 18 | Initialization is a fairly expensive process, which involves among 19 | other things topologically sorting class hierarchies, assigning slots 20 | to multi-methods and computing dispatch tables. `initialize` 21 | attempts to be conservative and re-compute only what needs be; 22 | however, adding or removing a single class or multi-method 23 | entails the re-examination of the entire class graph. 24 | 25 | [h3 Examples] 26 | 27 | `` 28 | 29 | int main() { 30 | yorel::methods::initialize(); 31 | // ... 32 | dlopen(...); // dynamically load a shared object 33 | yorel::methods::initialize(); // if classes, multi-methods or specializations were added 34 | // ... 35 | } 36 | 37 | `` 38 | 39 | [endsect] 40 | -------------------------------------------------------------------------------- /doc/installation.qbk: -------------------------------------------------------------------------------- 1 | [section Installation] 2 | 3 | The library is available from [@https://github.com/jll63/yomm11 4 | GitHub]. 5 | 6 | The library is entirely self-contained; all you need is a C++11 7 | capable compiler and optionally cmake. 8 | 9 | The library consists in a static archive - libyomm11.a - and a set of 10 | header files. 11 | 12 | Run the following commands: 13 | 14 | cmake 15 | make 16 | make test 17 | make install 18 | 19 | These will build, test and install libyomm11.a and the header files in 20 | the default locations. You can override this by passing 21 | `-DCMAKE_INSTALL_PREFIX=` to cmake. The default compiler is 22 | g++. Pass `-DCMAKE_CXX_COMPILER=clang++` if you want to use Clang. 23 | 24 | Alternatively, you can add the library's `include` directory to the 25 | include path and compile and link `src/methods.cpp` directly 26 | into your application. 27 | 28 | Read [link methods.advanced_topics.dynamic_linking this section] 29 | if you intend to use shared libraries or dynamic linking. 30 | 31 | [endsect] 32 | -------------------------------------------------------------------------------- /doc/intro.qbk: -------------------------------------------------------------------------------- 1 | [/ -*- compile-command: "bjam" -*-; auto-fill-mode: t /] 2 | 3 | [section Introduction] 4 | Virtual functions allow us to select a function depending on the dynamic type of the object on which the method is called. I often use this metaphore when teaching polymorphism: if you kick a dog, it barks; if you kick a cat, it meows. The reaction you get depends on the animal's nature. Schematically: 5 | 6 | `` 7 | kick(dog) -> bow wow wow 8 | kick(cat) -> meow, scratch 9 | `` 10 | Sometimes, however, we would like to select a function based on the 11 | type of two arguments. To carry on with the animal metaphore, imagine 12 | that you want to model the encounter of two animals: 13 | 14 | `` 15 | encounter(animal, animal) -> ignore 16 | encounter(cow, wolf) -> run 17 | encounter(wolf, cow) -> hunt 18 | encounter(wolf, wolf) -> wag tail 19 | `` 20 | 21 | On a more serious side, think the addition of two matrices whose exact 22 | type (plain, diagonal, symmetrical) is not known at compile time: 23 | 24 | `` 25 | add(matrix, matrix) -> add elements one by one 26 | add(diagonal, matrix) -> no need to add all the zeroes 27 | add(diagonal, diagonal) -> just add the two diagonals 28 | `` 29 | 30 | The problem may even involve a higher number of dynamic arguments, for 31 | example an expense approval system that depends on the nature of the 32 | expense, the reason and the role of the person spending the money: 33 | 34 | `` 35 | approve(expense, reason, role) -> false - the default 36 | approve(plane, business, executive) -> true 37 | approve(bus, business, employee) -> true 38 | approve(expense, reason, ceo) -> true 39 | `` 40 | 41 | These sorts of problems are easily addressed in languages like Common 42 | Lisp, Dylan or Clojure: they support something called multi-methods, 43 | which are just like virtual functions without the limitation of being 44 | able to dispatch a call on the basis of one argument only. 45 | 46 | C++ programmers have to rely instead on cumbersome techniques, like double dispatch or runtime type enquiry. An extension of C++ - Cmm - implements multi-methods, but requires using an ad-hoc compiler. 47 | 48 | When asked about which features he likes in other languages, Bjarne 49 | Stroustrup regularly cites multi-methods. Several years ago, he 50 | co-authored a paper describing an extension of C++ in that direction: 51 | "Open Multi-Methods for C++". 52 | 53 | Now what is an /open/ multi-method? It is simply a multi-method that is 54 | defined outside of any class, like a (non-member) function. Dogma 55 | aside, this is very useful because it gives you "polymorphism from 56 | outside" - it un-ties two good features, namely polymorphism and 57 | encapsulation, that are unnecessarily tied together in C++. Let's look 58 | at one more example: we want to create a user interface element for a 59 | business object. Of course the user interface depends on the object's 60 | type. Conventional solutions include: using an abstract factory, 61 | sticking a virtual function that returns a dialog box in the business 62 | object (ugh). With an open "mono-method", the solution is simple and 63 | elegant: 64 | 65 | `` 66 | make_dialog(natural_person) -> return new natural_person_dialog() 67 | make_dialog(legal_person) -> return new legal_person_dialog() 68 | make_dialog(audience) -> ... 69 | ... 70 | `` 71 | 72 | This library is pretty much an implementation of what Stroustrup et al 73 | describe in their paper, done entirely using the existing C++11 74 | features. 75 | 76 | [endsect] 77 | -------------------------------------------------------------------------------- /doc/multi_method.qbk: -------------------------------------------------------------------------------- 1 | [section multi_method] 2 | 3 | [h3 Synopsis] 4 | `` 5 | template Spec, typename __return_type__, typename __args__...> 6 | struct multimethod; // declared but not defined 7 | 8 | template __class_template__, typename __return_type__, typename __args__...> 9 | struct multimethod<__specialization__, __return_type__(__args__)> { 10 | __return_type__ operator ()(__args__...) const; 11 | static __return_type__ method(__args__...); 12 | static void specialize<__specialization__>(); 13 | }; 14 | `` 15 | [h3 Description] 16 | 17 | Defines a multi-method. 18 | 19 | Actual multi-methods are specializations of this template, defined 20 | only for argument lists matching the second synopsis. 21 | 22 | __class_template__ is a user-provided class template that allows for the 23 | creations of different multi-methods with the same signature. The 24 | template is then typically used to define specializations while 25 | averting namespace pollution. 26 | 27 | __args__ must contain at least one argument that includes the 28 | __virtual___ marker. The markers are removed from the signature of 29 | __operator ()__. 30 | 31 | Both `operator ()` and `method` implement method dispatch. The 32 | former is convenient to call a multi-method, the latter to take a 33 | pointer to it. Other than that, the behavior and the performance are 34 | identical. 35 | 36 | 37 | Multi-methods are stateless function objects. The data structures 38 | implementing specialization registration and method dispatch reside in 39 | static data members. Typically, a multi-method declaration/definition 40 | consists in a `constexpr` declaration of a specialization of 41 | `multi_method`. 42 | 43 | [h3 Example] 44 | 45 | [ref_multi_method] 46 | 47 | [endsect] 48 | -------------------------------------------------------------------------------- /doc/multi_method_operator_call.qbk: -------------------------------------------------------------------------------- 1 | [section:multi_method_operator_call multi_method::operator ()] 2 | 3 | [h3 Synopsis] 4 | 5 | __mm__(__args__...); 6 | 7 | [h3 Description] 8 | 9 | Calls a multi-method. The most specific specializatoin is used. 10 | 11 | If no specialization accepting __args__ exists, an exception of type 12 | __undefined__ is thrown. If one or more acceptable specializations 13 | exists, but none of them is more specific than all the others, an 14 | exception of type __ambiguous__ is thrown. 15 | 16 | [h3 Example] 17 | 18 | `` 19 | approve(plane(), ceo(), comfort()); 20 | `` 21 | 22 | [endsect] 23 | -------------------------------------------------------------------------------- /doc/multi_method_specialize.qbk: -------------------------------------------------------------------------------- 1 | [section:multi_method_specialize multi_method::specialize] 2 | 3 | [h3 Synopsis] 4 | 5 | __mm__<__class_template__, __return_type__(__args__...)>::specialize<__specialization__>(); 6 | 7 | [h3 Description] 8 | 9 | Adds a specialization to a multi-method. 10 | 11 | __specialization__ must be a class with the following pubic members: 12 | 13 | * `static __return_type__ body(__args__...);` 14 | 15 | * `static __return_type__ (*next)(__args__...);` 16 | 17 | Typically, __specialization__ is a specialization of __class_template__. 18 | 19 | [h3 Example] 20 | 21 | After example from [link methods.reference.specializing.multi_method_specialize]: 22 | [ref_spec] 23 | // in main() or in a static constructor: 24 | [ref_spec_call] 25 | 26 | [endsect] 27 | -------------------------------------------------------------------------------- /doc/multi_methods.qbk: -------------------------------------------------------------------------------- 1 | [/ -*- compile-command: "bjam" -*-; auto-fill-mode: t /] 2 | [/============================================================================== 3 | Copyright Jean-Louis Leroy 2013. 4 | Use, modification, and distribution are subject to the Boost Software License, Version 1.0. 5 | ===============================================================================/] 6 | [article Multi-methods 7 | [quickbook 1.5] 8 | [id methods] 9 | [version 1.0] 10 | [authors [Leroy, Jean-Louis]] 11 | [copyright 2013 Jean-Louis Leroy] 12 | [license 13 | Distributed under the Boost Software License, Version 1.0. 14 | (See accompanying file LICENSE_1_0.txt or copy at 15 | [@http://www.boost.org/LICENSE_1_0.txt])]] 16 | 17 | Support: [@https://groups.google.com/forum/#!forum/yomm11 YOMM11 on Google Groups] 18 | 19 | [include intro.qbk] 20 | 21 | [include installation.qbk] 22 | 23 | [include tutorial.qbk] 24 | 25 | [include advanced_topics.qbk] 26 | 27 | [include reference.qbk] 28 | 29 | [section Acknowledgements] 30 | 31 | This library is strongly inspired by Pirkelbauer, Solodkyy and 32 | Stroustrup's paper [@http://www.stroustrup.com/multimethods.pdf "Open Multi-Methods for C++"]. 33 | 34 | The paper [@http://hal.inria.fr/docs/00/07/37/21/PDF/RR-2977.pdf 35 | "Fast algorithms for compressed multi-method dispatch tables 36 | generation"] by Amiel, Dujardin and Simon was influential in the design 37 | of the algorithm for dispatch table generation. 38 | 39 | [endsect] 40 | 41 | -------------------------------------------------------------------------------- /doc/next.cpp: -------------------------------------------------------------------------------- 1 | // next.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // Example taken Dylan's documentation, see http://opendylan.org/documentation/intro-dylan/multiple-dispatch.html 8 | 9 | #include 10 | 11 | using namespace std; 12 | 13 | //[ prologue 14 | #include 15 | 16 | using yorel::methods::virtual_; 17 | using yorel::methods::selector; 18 | //] 19 | 20 | 21 | //[ vehicle 22 | struct Vehicle : selector { 23 | YOMM11_CLASS(Vehicle); 24 | Vehicle() { 25 | YOMM11_INIT(); 26 | } 27 | }; 28 | //] 29 | 30 | //[ car 31 | struct Car : Vehicle { 32 | YOMM11_CLASS(Car, Vehicle); 33 | Car() { 34 | YOMM11_INIT(); 35 | } 36 | }; 37 | //] 38 | struct Truck : Vehicle { 39 | YOMM11_CLASS(Truck, Vehicle); 40 | Truck() { 41 | YOMM11_INIT(); 42 | } 43 | }; 44 | 45 | struct Inspector : selector { 46 | YOMM11_CLASS(Inspector); 47 | Inspector() { 48 | YOMM11_INIT(); 49 | } 50 | }; 51 | 52 | struct StateInspector : Inspector { 53 | YOMM11_CLASS(StateInspector, Inspector); 54 | StateInspector() { 55 | YOMM11_INIT(); 56 | } 57 | }; 58 | 59 | //[ inspect 60 | YOMM11_METHOD(inspect, void, virtual_&, virtual_&); 61 | //] 62 | 63 | //[ specialization 64 | YOMM11_SPECIALIZATION(inspect, void, Vehicle& v, Inspector& i) { 65 | cout << "Inspect vehicle.\n"; 66 | } YOMM11_END_SPECIALIZATION; 67 | //] 68 | 69 | //[ next 70 | YOMM11_SPECIALIZATION(inspect, void, Car& v, Inspector& i) { 71 | next(v, i); 72 | cout << "Inspect seat belts.\n"; 73 | } YOMM11_END_SPECIALIZATION; 74 | 75 | YOMM11_SPECIALIZATION(inspect, void, Car& v, StateInspector& i) { 76 | next(v, i); 77 | cout << "Check road tax.\n"; 78 | } YOMM11_END_SPECIALIZATION; 79 | //] 80 | 81 | //[ call 82 | int main() { 83 | yorel::methods::initialize(); // IMPORTANT! - allocates slots and compute dispatch tables 84 | 85 | Vehicle&& vehicle1 = Car(); 86 | Inspector&& inspector1 = StateInspector(); 87 | Vehicle&& vehicle2 = Truck(); 88 | Inspector&& inspector2 = Inspector(); 89 | 90 | cout << "First inspection:\n"; 91 | inspect(vehicle1, inspector1); 92 | 93 | cout << "\nSecond inspection:\n"; 94 | inspect(vehicle2, inspector2); 95 | 96 | return 0; 97 | } 98 | //] 99 | -------------------------------------------------------------------------------- /doc/next.qbk: -------------------------------------------------------------------------------- 1 | [section next] 2 | 3 | [h3 Synopsis] 4 | 5 | next(__args__...) 6 | 7 | [h3 Description] 8 | 9 | Inside a specialization, calls the next most specific 10 | specialization, if it exists. 11 | 12 | `next` is a pointer to a function that takes the same argument list as 13 | the specialized multi-method, and returns the same type. 14 | 15 | If no such method exists, either because no less specific 16 | specialization exists, or because several less specific 17 | specializations exists but none of them is more specific than all the 18 | others, then `next` contains `nullptr`. 19 | 20 | [h3 Example] 21 | 22 | `` 23 | MULTI_METHOD(inspect, bool, virtual_&, virtual_&); 24 | 25 | BEGIN_SPECIALIZATION(inspect, bool, Car& v, Inspector& i) { 26 | ... 27 | bool ok = true; 28 | 29 | if (next) { 30 | ok = next(v, i); 31 | } 32 | ... 33 | } END_SPECIALIZATION; 34 | `` 35 | 36 | [endsect] 37 | -------------------------------------------------------------------------------- /doc/reference.qbk: -------------------------------------------------------------------------------- 1 | [/ -*- compile-command: "bjam" -*-; auto-fill-mode: t /] 2 | 3 | [section Reference] 4 | 5 | [def __name__ [~name]] 6 | [def __return_type__ [~return_type]] 7 | [def __args__ [~args]] 8 | [def __arg__ [~arg]] 9 | [def __class__ [~class]] 10 | [def __base__ [~base]] 11 | [def __bases__ [~bases]] 12 | [def __class_template__ [~class_template]] 13 | [def __specialization__ [~specialization]] 14 | [def __mm__ [~multi_method]] 15 | 16 | [def __selector__ [link methods.reference.registering_classes.selector `selector`]] 17 | [def __MM_CLASS_MULTI__ [link methods.reference.registering_classes.mm_class_multi `MM_CLASS_MULTI`]] 18 | [def __MM_CLASS__ [link methods.reference.registering_classes.mm_class `MM_CLASS`]] 19 | [def __MM_FOREIGN_CLASS__ [link methods.reference.registering_classes.mm_foreign_class `MM_FOREIGN_CLASS`]] 20 | [def __MM_INIT__ [link methods.reference.registering_classes.mm_init `MM_INIT`]] 21 | 22 | [def __virtual___ [link methods.reference.declaring_methods.virtual_ `virtual_`]] 23 | [def __MULTI_METHOD_MACRO__ [link methods.reference.declaring_methods.multi_method_macro `MULTI_METHOD_MACRO`]] 24 | [def __multi_method__ [link methods.reference.declaring_methods.multi_method `multi_method`]] 25 | [def __multi_method_specialize__ [link methods.reference.specializing.multi_method_specialize `multi_method::specialize`]] 26 | 27 | [def __initialize__ [link methods.reference.calling.initialize `initialize`]] 28 | [def __multi_method_operator_call__ [link methods.reference.calling.multi_method_operator_call `multi_method::operator ()`]] 29 | [def __undefined__ [link methods.reference.calling.undefined `undefined`]] 30 | [def __ambiguous__ [link methods.reference.calling.ambiguous `ambiguous`]] 31 | 32 | [import reference_examples.cpp] 33 | 34 | [section Registering classes] 35 | [include selector.qbk] 36 | [include MM_CLASS.qbk] 37 | [include MM_CLASS_MULTI.qbk] 38 | [include MM_FOREIGN_CLASS.qbk] 39 | [include MM_INIT.qbk] 40 | [endsect] 41 | 42 | [section Declaring multi-methods] 43 | [include MULTI_METHOD_MACRO.qbk] 44 | [include virtual_.qbk] 45 | [include multi_method.qbk] 46 | [endsect] 47 | 48 | [section Specializing] 49 | [include BEGIN_SPECIALIZATION_END_SPECIALIZATION.qbk] 50 | [include multi_method_specialize.qbk] 51 | [endsect] 52 | 53 | [section Calling] 54 | [include initialize.qbk] 55 | [include multi_method_operator_call.qbk] 56 | [include next.qbk] 57 | [include undefined.qbk] 58 | [include ambiguous.qbk] 59 | [endsect] 60 | 61 | [endsect] 62 | -------------------------------------------------------------------------------- /doc/reference_examples.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct role : yorel::methods::selector { 7 | YOMM11_CLASS(role); 8 | role() { 9 | YOMM11_INIT(); 10 | } 11 | }; 12 | 13 | struct manager : role { 14 | YOMM11_CLASS(manager, role); 15 | manager() { 16 | YOMM11_INIT(); 17 | } 18 | }; 19 | 20 | struct ceo : role { 21 | YOMM11_CLASS(ceo, role); 22 | ceo() { 23 | YOMM11_INIT(); 24 | } 25 | }; 26 | 27 | struct expense : yorel::methods::selector { 28 | YOMM11_CLASS(expense); 29 | expense() { 30 | YOMM11_INIT(); 31 | } 32 | }; 33 | 34 | struct plane : expense { 35 | YOMM11_CLASS(plane, expense); 36 | plane() { 37 | YOMM11_INIT(); 38 | } 39 | }; 40 | 41 | struct cab : expense { 42 | YOMM11_CLASS(cab, expense); 43 | cab() { 44 | YOMM11_INIT(); 45 | } 46 | }; 47 | 48 | struct reason : yorel::methods::selector { 49 | YOMM11_CLASS(reason); 50 | reason() { 51 | YOMM11_INIT(); 52 | } 53 | }; 54 | 55 | struct business : reason { 56 | YOMM11_CLASS(business, reason); 57 | business() { 58 | YOMM11_INIT(); 59 | } 60 | }; 61 | 62 | struct comfort : reason { 63 | YOMM11_CLASS(comfort, reason); 64 | comfort() { 65 | YOMM11_INIT(); 66 | } 67 | }; 68 | 69 | using namespace yorel::methods; 70 | 71 | //[ ref_method 72 | 73 | template struct approve_specialization; 74 | 75 | constexpr method< 76 | approve_specialization, 77 | bool(const virtual_&, const virtual_&, const virtual_&) 78 | > approve; 79 | //] 80 | 81 | //[ ref_spec 82 | 83 | struct approve_ceo_all { 84 | static bool (*next)(const expense&, const role&, const reason&); 85 | static bool body(const plane& what, const ceo& who, const reason& why) { 86 | return true; 87 | } 88 | }; 89 | //] 90 | 91 | bool (*approve_ceo_all::next)(const expense&, const role&, const reason&); 92 | 93 | int main() { 94 | //[ ref_spec_call 95 | approve.specialize(); 96 | //] 97 | initialize(); 98 | cout << approve(plane(), ceo(), comfort()) << endl; 99 | } 100 | -------------------------------------------------------------------------------- /doc/selector.qbk: -------------------------------------------------------------------------------- 1 | [section selector] 2 | 3 | [h3 Synopsis] 4 | 5 | class [~MyClass] : public selector { ... }; 6 | 7 | [h3 Description] 8 | 9 | Achieve optimal performance by lending space for a pointer to the dispatcher. 10 | 11 | Inherit from class `selector` to achieve best performance in 12 | multi-method dispatch. It contains one name - `_mm_init_class` - which 13 | is an implementation detail. 14 | 15 | [endsect] 16 | -------------------------------------------------------------------------------- /doc/tutorial.qbk: -------------------------------------------------------------------------------- 1 | [/ -*- compile-command: "bjam" -*-; auto-fill-mode: t /] 2 | 3 | [section Tutorial] 4 | 5 | [import next.cpp] 6 | 7 | This tutorial shows how to create a multi-method, specialize it and 8 | call it. It is a based on an 9 | [@http://opendylan.org/documentation/intro-dylan/multiple-dispatch.html 10 | example] from the documentation of Dylan, a language that natively 11 | supports multi-methods. The example implements multi-method `inspect()`, which takes two 12 | arguments: a Vehicle (either a Car or a Truck) and an Inspector 13 | (either a regular inspector or a more picky State inspector). Depending on the 14 | type of the vehicle, different checks are be performed. A State inspector 15 | will also check if the road tax has been paid. 16 | 17 | [heading Accessing the library] 18 | 19 | The library is accessed by including the header and possibly importing 20 | names in the current namespace: 21 | 22 | [prologue] 23 | 24 | The library exposes its facilities mostly via a small set of 25 | macros, although it is possible - and sometimes necessary - to bypass 26 | them. 27 | 28 | The `virtual_` modifier denotes the parameters of a multi-method that 29 | determine the selection of the specialization to be called. It is used 30 | quite frequently, so it makes sense to import it in the current 31 | namespace. You may also want to import the class `selector` (see 32 | below). Other than that, if you stick to the macros, it does not make 33 | much sense to import the `yorel::methods` namespace as a whole. 34 | 35 | [heading Declaring classes] 36 | 37 | The library needs to know about the inheritance relationships between 38 | the classes involved in multi-methods. Currently C++ does not make it 39 | possible to infer it from class declarations, so you will need to 40 | provide the information. 41 | 42 | You will get the best performance if your classes collaborate with the 43 | framework. Essentially, this amounts to hosting a pointer to a 44 | multi-method table and initializing it in the constructors. In that 45 | case, multi-method calls will be nearly as fast as virtual function 46 | calls if there is a single virtual argument - and much faster than 47 | solutions based on multiple dispatch otherwise. Under this scheme, the 48 | roots of the class hierarchies derive from `yorel::methods::selector`. 49 | 50 | The definition of class `Vehicle` thus looks like this: 51 | 52 | [vehicle] 53 | 54 | For classes `Car` and `Truck` we specify the base classes as an extra 55 | argument to the MM_CLASS macro. In our example, there is only one base 56 | - `Vehicle` - but the library supports multiple and virtual 57 | inheritance, although repeated inheritance is not allowed (yet). 58 | 59 | Class `Car` will thus look like this: 60 | 61 | [car] 62 | 63 | ...and likewise for the Inspector hierarchy. 64 | 65 | [heading Using existing classes, without modifications] 66 | 67 | Sometimes it is not possible to modify the source of a hierarchy. In 68 | that case, we can declare the inheritance relationships "from the 69 | outside", via the `MM_FOREIGN_CLASS` macro: 70 | 71 | `` 72 | struct Vehicle { 73 | // ... 74 | }; 75 | 76 | struct Car { 77 | // ... 78 | }; 79 | 80 | MM_FOREIGN_CLASS(Vehicle); 81 | MM_FOREIGN_CLASS(Car, Vehicle); 82 | 83 | `` 84 | The classes must be polymorpic, i.e. have at least one virtual function. 85 | 86 | [heading Declaring multi-methods] 87 | 88 | Multi-methods are declared via the `MULTI_METHOD` macro: 89 | 90 | [inspect] 91 | 92 | The first parameter is the multi-method name. The second is the return 93 | type. The other parameters are the multi-method's 94 | arguments. 95 | 96 | Parameters that intervene in the selection of the 97 | specialization are decorated with the `virtual_` class 98 | template. Virtual parameters must be references to classes declared as 99 | above. Thus the form of a virtual parameter is either 100 | `virtual_&` or `const virtual_&`. 101 | 102 | There is no artifical limitation to the number of virtual 103 | parameters. They can be freely mixed with non-virtual parameters. 104 | 105 | The MULTI_METHOD declaration can be placed in a header file. 106 | 107 | Currently, a multi-method cannot be overloaded. 108 | 109 | [heading Defining specializations] 110 | 111 | Specialization are defined via the two `_SPECIALIZATION` macros: 112 | 113 | [specialization] 114 | 115 | Here we define a behavior for the most general case. The arguments 116 | passed to the macro are again the multi-method name, the return type, 117 | and a list of arguments. Note that the `virtual_` speficier is not 118 | used in specializations. 119 | 120 | Specializations normally go in implementation files. 121 | 122 | [heading Calling the next specialization] 123 | 124 | The previous "specialization" dealt in fact with the most general 125 | case. Here are two more: 126 | 127 | [next] 128 | 129 | Note the expression `next(v, i)`. In ordinary virtual functions, you 130 | often want to call the function that is overriden as part of the 131 | overriding function. Multi-methods have a similar notion of 132 | "super-method". Inside a specialization, the variable `next` contains 133 | a pointer to the next most specific specialization after the one being 134 | executed - if it exists and is unique. 135 | 136 | [heading Calling multi-methods] 137 | 138 | Before actually calling any multi-method, the 139 | `yorel::methods::initialize()` function must be called. It must also be 140 | called whenever classes or methods are added or removed, for example 141 | when loading or un-loading dynamic libraries. `initialize()` builds the 142 | dispatch tables for all the classes and all the multi-methods that 143 | have been modified since the last time it was called or since the 144 | program has started. 145 | 146 | Once that is done, our `inspect()` multi-method can be called just 147 | like any ordinary function - or rather, function object: 148 | 149 | [call] 150 | 151 | The output is: 152 | 153 | `` 154 | First inspection: 155 | Inspect vehicle. 156 | Inspect seat belts. 157 | Check road tax. 158 | 159 | Second inspection: 160 | Inspect vehicle. 161 | `` 162 | 163 | Note that what is happening here has nothing to do with function 164 | overloading: the real type of the objects are hidden. Multi-method 165 | dispatch happens at run time. 166 | 167 | [heading Where to go next] 168 | 169 | You may want to check the other examples in the `examples` 170 | directory. They feature: non-virtual arguments; a multi-method with 171 | three virtual arguments; and foreign classes. 172 | 173 | [@examples/asteroids.cpp asteroids.cpp] 174 | 175 | [@examples/three.cpp three.cpp] 176 | 177 | [@examples/foreign.cpp foreign.cpp] 178 | 179 | [endsect] 180 | -------------------------------------------------------------------------------- /doc/undefined.qbk: -------------------------------------------------------------------------------- 1 | [section undefined] 2 | 3 | [h3 Synopsis] 4 | 5 | try { 6 | __mm__(__args__...); 7 | } catch (undefined&) { 8 | ... 9 | } 10 | 11 | [h3 Description] 12 | 13 | Thrown by __multi_method_operator_call__ when no specialization exists 14 | for an actual argument list. 15 | 16 | [h3 Example] 17 | 18 | See synopsis. 19 | 20 | [endsect] 21 | -------------------------------------------------------------------------------- /doc/upload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | scp -r html/index.html html/multi_methods/ yorel@yorel.be:www/mm 3 | scp -r html/examples/*.?pp yorel@yorel.be:www/mm/examples 4 | 5 | -------------------------------------------------------------------------------- /doc/virtual_.qbk: -------------------------------------------------------------------------------- 1 | [section virtual_] 2 | 3 | [h3 Synopsis] 4 | virtual_<__class__> 5 | 6 | [h3 Description] 7 | 8 | `virtual_` denotes virtual arguments in a multi-method declaration. 9 | 10 | __class__ must be a class type registered via one of the `MM_*CLASS` 11 | macros. It can appear only in multi-method argument lists to declare a 12 | reference to an object, constant or not. In other words, `virtual_` 13 | may be used only in the following manner: 14 | 15 | virtual_& 16 | const virtual_& 17 | 18 | [endsect] 19 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt 2 | # Copyright (c) 2013 Jean-Louis Leroy 3 | # Distributed under the Boost Software License, Version 1.0. (See 4 | # accompanying file LICENSE_1_0.txt or copy at 5 | # http:#www.boost.org/LICENSE_1_0.txt) 6 | 7 | add_executable(asteroids asteroids.cpp) 8 | target_link_libraries (asteroids yomm11) 9 | 10 | add_executable(three three.cpp) 11 | target_link_libraries (three yomm11) 12 | 13 | add_executable(next next.cpp) 14 | target_link_libraries (next yomm11) 15 | 16 | add_executable(foreign foreign.cpp) 17 | target_link_libraries (foreign yomm11) 18 | 19 | add_executable(matrix matrix.cpp) 20 | target_link_libraries (matrix yomm11) 21 | 22 | if(NOT MSVC) 23 | add_executable(dl_main dl_main.cpp) 24 | add_library( dl_shared SHARED dl_shared.cpp ) 25 | if(APPLE) 26 | set_target_properties(dl_main PROPERTIES LINK_FLAGS "-Wl,-export_dynamic") 27 | set_target_properties(dl_shared PROPERTIES LINK_FLAGS "-Wl,-undefined,dynamic_lookup") 28 | endif() 29 | target_link_libraries (dl_main yomm11 dl dl_shared) 30 | endif() 31 | -------------------------------------------------------------------------------- /examples/asteroids.cpp: -------------------------------------------------------------------------------- 1 | // asteroids.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | using yorel::multi_methods::virtual_; 14 | 15 | struct object : yorel::multi_methods::selector { 16 | MM_CLASS(object); 17 | object() { 18 | MM_INIT(); 19 | } 20 | }; 21 | 22 | struct ship : object { 23 | MM_CLASS(ship, object); 24 | ship() { 25 | MM_INIT(); 26 | } 27 | }; 28 | 29 | struct asteroid : object { 30 | MM_CLASS(asteroid, object); 31 | asteroid() { 32 | MM_INIT(); 33 | } 34 | }; 35 | 36 | struct saucer : object { 37 | MM_CLASS(saucer, object); 38 | saucer() { 39 | MM_INIT(); 40 | } 41 | }; 42 | 43 | struct bullet : object { 44 | MM_CLASS(bullet, object); 45 | bullet() { 46 | MM_INIT(); 47 | } 48 | }; 49 | 50 | MULTI_METHOD(collide, string, virtual_&, virtual_&, bool swapped); 51 | 52 | BEGIN_SPECIALIZATION(collide, string, object& a, object& b, bool swapped) { 53 | if (swapped) { 54 | return "kaboom!"; 55 | } else { 56 | return collide(b, a, true); 57 | } 58 | } END_SPECIALIZATION; 59 | 60 | BEGIN_SPECIALIZATION(collide, string, asteroid& a, asteroid& b, bool swapped) { 61 | return "traverse"; 62 | } END_SPECIALIZATION; 63 | 64 | int main() { 65 | yorel::multi_methods::initialize(); // IMPORTANT! 66 | 67 | ship player; 68 | asteroid as; 69 | saucer small; 70 | bullet b; 71 | 72 | cout << collide(player, as, false) << endl; // kaboom! 73 | cout << collide(as, as, false) << endl; // traverse 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /examples/dl.hpp: -------------------------------------------------------------------------------- 1 | // -*- compile-command: "make dl_main dl_shared.so && ./dl_main" -*- 2 | 3 | // dl.hpp 4 | // Copyright (c) 2013 Jean-Louis Leroy 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef DL_DEFINED 10 | #define DL_DEFINED 11 | 12 | #include 13 | 14 | #include 15 | 16 | #ifdef SHARED 17 | #include 18 | #endif 19 | 20 | struct Animal : yorel::multi_methods::selector { 21 | MM_CLASS(Animal); 22 | Animal() { 23 | MM_INIT(); 24 | } 25 | }; 26 | 27 | MM_EXTERN_CLASS(Animal); 28 | 29 | struct Herbivore : Animal { 30 | MM_CLASS(Herbivore, Animal); 31 | Herbivore() { 32 | MM_INIT(); 33 | } 34 | }; 35 | 36 | MM_EXTERN_CLASS(Herbivore); 37 | 38 | struct Carnivore : Animal { 39 | MM_CLASS(Carnivore, Animal); 40 | Carnivore() { 41 | MM_INIT(); 42 | } 43 | }; 44 | 45 | MM_EXTERN_CLASS(Carnivore); 46 | 47 | struct Cow : Herbivore { 48 | MM_CLASS(Cow, Herbivore); 49 | Cow() { 50 | MM_INIT(); 51 | } 52 | }; 53 | 54 | MM_EXTERN_CLASS(Cow); 55 | 56 | struct Wolf : Carnivore { 57 | MM_CLASS(Wolf, Carnivore); 58 | Wolf() { 59 | MM_INIT(); 60 | } 61 | }; 62 | 63 | MM_EXTERN_CLASS(Wolf); 64 | 65 | MULTI_METHOD(encounter, std::string, const yorel::multi_methods::virtual_&, const yorel::multi_methods::virtual_&); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /examples/dl_main.cpp: -------------------------------------------------------------------------------- 1 | // dl_main.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /* compile with the following commands: 8 | g++ -std=c++11 -I../include -c dl_main.cpp 9 | g++ -std=c++11 -I../include -c ../src/multi_methods.cpp 10 | g++ dl_main.o multi_methods.o -o dl_main -ldl -rdynamic 11 | g++ -std=c++11 -I../include -fPIC -c -o dl_shared.o dl_shared.cpp 12 | g++ -shared -Wl,-soname,dl_shared.so -o dl_shared.so dl_shared.o 13 | */ 14 | 15 | #include 16 | #include "dl.hpp" 17 | 18 | #include 19 | #include 20 | 21 | using namespace std; 22 | using yorel::multi_methods::virtual_; 23 | 24 | BEGIN_SPECIALIZATION(encounter, string, const Animal&, const Animal&) { 25 | return "ignore"; 26 | } END_SPECIALIZATION; 27 | 28 | BEGIN_SPECIALIZATION(encounter, string, const Herbivore&, const Carnivore&) { 29 | return "run"; 30 | } END_SPECIALIZATION; 31 | 32 | int main() { 33 | yorel::multi_methods::initialize(); // IMPORTANT! 34 | 35 | cout << "Before loading library\n"; 36 | cout << "encounter(Cow(), Wolf()) -> " << encounter(Cow(), Wolf()) << endl; 37 | cout << "encounter(Wolf(), Cow()) -> " << encounter(Wolf(), Cow()) << endl; 38 | 39 | void* handle = dlopen( 40 | #ifdef __APPLE__ 41 | "./libdl_shared.dylib" 42 | #else 43 | "./libdl_shared.so" 44 | #endif 45 | , RTLD_NOW); 46 | 47 | if (!handle) { 48 | cout << "dlopen() failed: " << dlerror() << "\n"; 49 | exit(1); 50 | } 51 | 52 | cout << "After loading library\n"; 53 | yorel::multi_methods::initialize(); // IMPORTANT! 54 | 55 | using make_tyget_type = Animal* (*)(); 56 | make_tyget_type make_tiger = reinterpret_cast(dlsym(handle, "make_tiger")); 57 | 58 | if (!make_tiger) { 59 | cout << "dlsym() failed: " << dlerror() << "\n"; 60 | exit(1); 61 | } 62 | 63 | cout << "encounter(Cow(), *make_tiger()) -> " << encounter(Cow(), *make_tiger()) << endl; 64 | cout << "encounter(Wolf(), Cow()) -> " << encounter(Wolf(), Cow()) << endl; 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /examples/dl_shared.cpp: -------------------------------------------------------------------------------- 1 | // dl_shared.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #define SHARED 8 | #include "dl.hpp" 9 | 10 | #include 11 | 12 | #include 13 | 14 | using namespace std; 15 | using yorel::multi_methods::virtual_; 16 | 17 | struct Tiger : Carnivore { 18 | MM_CLASS(Tiger, Carnivore); 19 | Tiger() { 20 | MM_INIT(); 21 | } 22 | }; 23 | 24 | extern "C" Tiger* make_tiger() { 25 | return new Tiger; 26 | } 27 | 28 | BEGIN_SPECIALIZATION(encounter, string, const Carnivore&, const Herbivore&) { 29 | return "hunt"; 30 | } END_SPECIALIZATION; 31 | -------------------------------------------------------------------------------- /examples/foreign.cpp: -------------------------------------------------------------------------------- 1 | // clang++ -g -std=c++11 -I../include ../src/methods.cpp foreign.cpp -o foreign && ./foreign 2 | // foreign.cpp 3 | // Copyright (c) 2013 Jean-Louis Leroy 4 | // Distributed under the Boost Software License, Version 1.0. (See 5 | // accompanying file LICENSE_1_0.txt or copy at 6 | // http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #include 9 | 10 | #include 11 | 12 | using namespace std; 13 | 14 | using yorel::multi_methods::virtual_; 15 | using yorel::multi_methods::selector; 16 | 17 | // following hierarchy does not collaborate with multi-methods 18 | 19 | struct role { 20 | virtual ~role() { } 21 | }; 22 | 23 | MM_FOREIGN_CLASS(role); 24 | 25 | struct manager : role { 26 | }; 27 | 28 | MM_FOREIGN_CLASS(manager, role); 29 | 30 | struct ceo : role { 31 | }; 32 | 33 | MM_FOREIGN_CLASS(ceo, role); 34 | 35 | // this one does 36 | 37 | struct expense : selector { 38 | MM_CLASS(expense); 39 | expense() { 40 | MM_INIT(); 41 | } 42 | }; 43 | 44 | struct plane : expense { 45 | MM_CLASS(plane, expense); 46 | plane() { 47 | MM_INIT(); 48 | } 49 | }; 50 | 51 | struct cab : expense { 52 | MM_CLASS(cab, expense); 53 | cab() { 54 | MM_INIT(); 55 | } 56 | }; 57 | 58 | // this one does not 59 | 60 | struct reason { 61 | virtual ~reason() { } 62 | }; 63 | 64 | MM_FOREIGN_CLASS(reason); 65 | 66 | struct business : reason { 67 | }; 68 | 69 | MM_FOREIGN_CLASS(business, reason); 70 | 71 | struct comfort : reason { 72 | }; 73 | 74 | MM_FOREIGN_CLASS(comfort, reason); 75 | 76 | // collaborating and foreign classes can appear in same multi-method definition 77 | 78 | MULTI_METHOD(approve, bool, const virtual_&, const virtual_&, const virtual_&); 79 | 80 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const role&, const reason&) { 81 | return false; 82 | } END_SPECIALIZATION; 83 | 84 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const ceo&, const reason&) { 85 | return true; 86 | } END_SPECIALIZATION; 87 | 88 | BEGIN_SPECIALIZATION(approve, bool, const cab&, const manager&, const business&) { 89 | return true; 90 | } END_SPECIALIZATION; 91 | 92 | #define demo(exp) cout << #exp << " -> " << exp << endl 93 | 94 | int main() { 95 | yorel::multi_methods::initialize(); // IMPORTANT! 96 | cout << boolalpha; 97 | 98 | // ceo does as he pleases 99 | demo( approve(plane(), ceo(), comfort()) ); // true 100 | demo( approve(cab(), ceo(), business()) ); // true 101 | // managers only take cabs for business 102 | demo( approve(cab(), manager(), business()) ); // true 103 | demo( approve(cab(), manager(), comfort()) ); // false 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /examples/matrix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using yorel::multi_methods::virtual_; 8 | using yorel::multi_methods::selector; 9 | 10 | namespace matrix_lib { 11 | 12 | class matrix; 13 | 14 | using any_matrix = shared_ptr; 15 | 16 | class matrix : public yorel::multi_methods::selector { 17 | public: 18 | MM_CLASS(matrix); 19 | 20 | matrix(int rows, int cols) : nr(rows), nc(cols) { 21 | MM_INIT(); 22 | } 23 | 24 | int rows() const { return nr; } 25 | int cols() const { return nc; } 26 | 27 | virtual double get(int r, int c) const = 0; 28 | virtual const vector& elements(vector&) const = 0; 29 | 30 | private: 31 | const int nr, nc; 32 | }; 33 | 34 | MULTI_METHOD(add, any_matrix, const virtual_& m1, const virtual_& m2); 35 | 36 | any_matrix operator +(const any_matrix& m1, const any_matrix& m2) { 37 | return add(*m1, *m2); 38 | } 39 | 40 | MULTI_METHOD(write, void, ostream& os, const virtual_& m); 41 | 42 | ostream& operator <<(ostream& os, const any_matrix& m) { 43 | write(os, *m); 44 | return os; 45 | } 46 | 47 | BEGIN_SPECIALIZATION(write, void, ostream& os, const matrix& m) { 48 | for (int r = 0; r < m.rows(); r++) { 49 | const char* sep = ""; 50 | for (int c = 0; c < m.cols(); c++) { 51 | os << sep << m.get(r, c); 52 | sep = " "; 53 | } 54 | os << "\n"; 55 | } 56 | } END_SPECIALIZATION; 57 | 58 | class ordinary : public matrix { 59 | public: 60 | MM_CLASS(ordinary, matrix); 61 | ordinary(int rows, int cols); 62 | template 63 | ordinary(int rows, int cols, Iter init); 64 | virtual double get(int r, int c) const { return data[r * cols() + c]; } 65 | const vector& elements(vector&) const { return data; } 66 | private: 67 | vector data; 68 | }; 69 | 70 | ordinary::ordinary(int rows, int cols) : matrix(rows, cols), data(rows * cols) { 71 | MM_INIT(); 72 | } 73 | 74 | template 75 | ordinary::ordinary(int rows, int cols, Iter init) : matrix(rows, cols), data(init, init + rows * cols) { 76 | MM_INIT(); 77 | } 78 | 79 | BEGIN_SPECIALIZATION(add, any_matrix, const matrix& m1, const matrix& m2) { 80 | vector result(m1.rows() * m1.cols()); 81 | vector v1, v2; 82 | const vector& elt1 = m1.elements(v1); 83 | const vector& elt2 = m2.elements(v2); 84 | transform(elt1.begin(), elt1.end(), elt2.begin(), result.begin(), plus()); 85 | return make_shared(m1.rows(), m1.cols(), result.begin()); 86 | } END_SPECIALIZATION; 87 | 88 | BEGIN_SPECIALIZATION(write, void, ostream& os, const ordinary& m) { 89 | os << "ordinary\n"; 90 | next(os, m); 91 | } END_SPECIALIZATION; 92 | 93 | class diagonal : public matrix { 94 | public: 95 | MM_CLASS(diagonal, matrix); 96 | template 97 | diagonal(int rows, Iter init); 98 | virtual double get(int r, int c) const { return r == c ? data[r] : 0; } 99 | const vector& elements(vector&) const; 100 | const vector& get_diagonal() const { return data; } 101 | private: 102 | vector data; 103 | }; 104 | 105 | template 106 | diagonal::diagonal(int rows, Iter init) : matrix(rows, rows), data(init, init + rows) { 107 | MM_INIT(); 108 | } 109 | 110 | const vector& diagonal::elements(vector& v) const { 111 | const int n = data.size(); 112 | v.resize(n * n); 113 | int offset{0}; 114 | for (double elt : data) { 115 | v[offset] = elt; 116 | offset += n + 1; 117 | } 118 | 119 | return v; 120 | } 121 | 122 | BEGIN_SPECIALIZATION(add, any_matrix, const diagonal& m1, const diagonal& m2) { 123 | vector result(m1.get_diagonal().size()); 124 | transform(m1.get_diagonal().begin(), m1.get_diagonal().end(), 125 | m2.get_diagonal().begin(), result.begin(), plus()); 126 | return make_shared(result.size(), result.begin()); 127 | } END_SPECIALIZATION; 128 | 129 | BEGIN_SPECIALIZATION(write, void, ostream& os, const diagonal& m) { 130 | os << "diagonal\n"; 131 | next(os, m); 132 | } END_SPECIALIZATION; 133 | 134 | class tridiagonal : public matrix { 135 | public: 136 | MM_CLASS(tridiagonal, matrix); 137 | template 138 | tridiagonal(int rows, Iter1 middle, Iter2 upper, Iter3 lower); 139 | virtual double get(int r, int c) const; 140 | const vector& elements(vector&) const; 141 | const vector& middle() const { return d1; } 142 | const vector& upper() const { return d2; } 143 | const vector& lower() const { return d3; } 144 | private: 145 | vector d1, d2, d3; 146 | }; 147 | 148 | template 149 | tridiagonal::tridiagonal(int rows, Iter1 middle, Iter2 upper, Iter3 lower) : matrix(rows, rows), 150 | d1(middle, middle + rows), d2(upper, upper + (rows - 1)), d3(lower, lower + (rows - 1)) { 151 | MM_INIT(); 152 | } 153 | 154 | double tridiagonal::get(int r, int c) const { 155 | return c == r ? middle()[r] : c == r + 1 ? upper()[r] : c == r - 1 ? lower()[c] : 0; 156 | } 157 | 158 | const vector& tridiagonal::elements(vector& v) const { 159 | const int n = middle().size(); 160 | v.resize(n * n); 161 | 162 | for (auto diag : { make_tuple(middle(), 0), make_tuple(upper(), 1), make_tuple(lower(), n) }) { 163 | int offset{std::get<1>(diag)}; 164 | for (double elt : std::get<0>(diag)) { 165 | v[offset] = elt; 166 | offset += n + 1; 167 | } 168 | } 169 | 170 | return v; 171 | } 172 | 173 | BEGIN_SPECIALIZATION(add, any_matrix, const tridiagonal& m1, const tridiagonal& m2) { 174 | int n = m1.middle().size(); 175 | vector middle(n), upper(n - 1), lower(n - 1); 176 | transform(m1.middle().begin(), m1.middle().end(), m2.middle().begin(), middle.begin(), plus()); 177 | transform(m1.upper().begin(), m1.upper().end(), m2.upper().begin(), upper.begin(), plus()); 178 | transform(m1.lower().begin(), m1.lower().end(), m2.lower().begin(), lower.begin(), plus()); 179 | return make_shared(middle.size(), middle.begin(), upper.begin(), lower.begin()); 180 | } END_SPECIALIZATION; 181 | 182 | BEGIN_SPECIALIZATION(write, void, ostream& os, const tridiagonal& m) { 183 | os << "tridiagonal\n"; 184 | next(os, m); 185 | } END_SPECIALIZATION; 186 | 187 | BEGIN_SPECIALIZATION(add, any_matrix, const tridiagonal& m1, const diagonal& m2) { 188 | double v[] = { 1 }; 189 | int n = m1.middle().size(); 190 | vector middle(n); 191 | transform(m1.middle().begin(), m1.middle().end(), m2.get_diagonal().begin(), middle.begin(), plus()); 192 | return make_shared(middle.size(), middle.begin(), m1.upper().begin(), m1.lower().begin()); 193 | } END_SPECIALIZATION; 194 | 195 | BEGIN_SPECIALIZATION(add, any_matrix, const diagonal& m1, const tridiagonal& m2) { 196 | return GET_SPECIALIZATION(add, any_matrix, const tridiagonal&, const diagonal&)(m2, m1); 197 | } END_SPECIALIZATION; 198 | 199 | // ambiguity example 200 | 201 | class sparse : public matrix { 202 | public: 203 | MM_CLASS(sparse, matrix); 204 | sparse(int rows, int cols); 205 | static any_matrix make(int rows, int cols); 206 | virtual double get(int r, int c) const { return 0; } 207 | const vector& elements(vector& data) const { return data; } 208 | private: 209 | // ... 210 | }; 211 | 212 | sparse::sparse(int rows, int cols) : matrix(rows, cols) { 213 | MM_INIT(); 214 | } 215 | 216 | any_matrix sparse::make(int rows, int cols) { 217 | return make_shared(rows, cols); 218 | } 219 | 220 | BEGIN_SPECIALIZATION(add, any_matrix, const sparse& m1, const matrix& m2) { 221 | return make_shared(m1.rows(), m1.cols()); 222 | } END_SPECIALIZATION; 223 | 224 | BEGIN_SPECIALIZATION(add, any_matrix, const matrix& m1, const diagonal& m2) { 225 | return make_shared(m1.rows(), m1.cols()); 226 | } END_SPECIALIZATION; 227 | 228 | BEGIN_SPECIALIZATION(add, any_matrix, const sparse& m1, const diagonal& m2) { 229 | return make_shared(m1.rows(), m1.cols()); 230 | } END_SPECIALIZATION; 231 | 232 | } 233 | 234 | using namespace matrix_lib; 235 | 236 | int main() { 237 | yorel::multi_methods::initialize(); 238 | 239 | cout << setw(8) << fixed << right << setprecision(0); 240 | 241 | double content[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 242 | 243 | { 244 | any_matrix m1 = make_shared(3, 3, content); 245 | any_matrix m2 = make_shared(3, content + 2); 246 | any_matrix sum = add(*m1, *m2); 247 | cout << sum << "\n"; 248 | } 249 | 250 | { 251 | any_matrix m1 = make_shared(3, content); 252 | any_matrix m2 = make_shared(3, content + 3); 253 | any_matrix sum = add(*m1, *m2); 254 | cout << sum << "\n"; 255 | } 256 | 257 | any_matrix m1 = make_shared(4, content); 258 | any_matrix m2 = make_shared(4, content, content + 1, content + 2); 259 | cout << m1 + m2 << "\n"; 260 | 261 | m1 = make_shared(3, 3) + make_shared(3, content); 262 | 263 | return 0; 264 | } 265 | -------------------------------------------------------------------------------- /examples/next.cpp: -------------------------------------------------------------------------------- 1 | // next.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // Example taken Dylan's documentation, see http://opendylan.org/documentation/intro-dylan/multiple-dispatch.html 8 | 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | using yorel::multi_methods::selector; 14 | using yorel::multi_methods::virtual_; 15 | 16 | // Vehicle is the root of a class hierarchy that includes support for fast dispatch. 17 | // That means: 18 | struct Vehicle : selector { // inherit from class selector 19 | MM_CLASS(Vehicle); // register class 20 | Vehicle() { 21 | MM_INIT(); // initialize pointer to multi-method table 22 | } 23 | }; 24 | 25 | struct Car : Vehicle { 26 | MM_CLASS(Car, Vehicle); // register class and its base 27 | Car() { 28 | MM_INIT(); 29 | } 30 | }; 31 | 32 | struct Truck : Vehicle { 33 | MM_CLASS(Truck, Vehicle); 34 | Truck() { 35 | MM_INIT(); 36 | } 37 | }; 38 | 39 | struct Inspector : selector { 40 | MM_CLASS(Inspector); 41 | Inspector() { 42 | MM_INIT(); 43 | } 44 | }; 45 | 46 | struct StateInspector : Inspector { 47 | MM_CLASS(StateInspector, Inspector); 48 | StateInspector() { 49 | MM_INIT(); 50 | } 51 | }; 52 | 53 | // declare a multi-method with two virtual arguments 54 | // this can go in a header file 55 | MULTI_METHOD(inspect, void, virtual_&, virtual_&); 56 | 57 | // define a specialization 58 | // this must go in an implementation file 59 | BEGIN_SPECIALIZATION(inspect, void, Vehicle& v, Inspector& i) { 60 | cout << "Inspect vehicle.\n"; 61 | } END_SPECIALIZATION; 62 | 63 | BEGIN_SPECIALIZATION(inspect, void, Car& v, Inspector& i) { 64 | next(v, i); // next calls the next most specialized method 65 | cout << "Inspect seat belts.\n"; 66 | } END_SPECIALIZATION; 67 | 68 | BEGIN_SPECIALIZATION(inspect, void, Car& v, StateInspector& i) { 69 | next(v, i); 70 | cout << "Check insurance.\n"; 71 | } END_SPECIALIZATION; 72 | 73 | int main() { 74 | yorel::multi_methods::initialize(); // IMPORTANT! - allocates slots and compute dispatch tables 75 | Car car; 76 | StateInspector inspector; 77 | // call method: 78 | inspect(car, inspector); // Inspect vehicle. Inspect seat belts. Check insurance. 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /examples/three.cpp: -------------------------------------------------------------------------------- 1 | // three.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | using namespace std; 12 | 13 | using yorel::multi_methods::virtual_; 14 | using yorel::multi_methods::selector; 15 | 16 | struct role : selector { 17 | MM_CLASS(role); 18 | role() { 19 | MM_INIT(); 20 | } 21 | }; 22 | 23 | struct manager : role { 24 | MM_CLASS(manager, role); 25 | manager() { 26 | MM_INIT(); 27 | } 28 | }; 29 | 30 | struct ceo : role { 31 | MM_CLASS(ceo, role); 32 | ceo() { 33 | MM_INIT(); 34 | } 35 | }; 36 | 37 | struct expense : selector { 38 | MM_CLASS(expense); 39 | expense() { 40 | MM_INIT(); 41 | } 42 | }; 43 | 44 | struct plane : expense { 45 | MM_CLASS(plane, expense); 46 | plane() { 47 | MM_INIT(); 48 | } 49 | }; 50 | 51 | struct cab : expense { 52 | MM_CLASS(cab, expense); 53 | cab() { 54 | MM_INIT(); 55 | } 56 | }; 57 | 58 | struct reason : selector { 59 | MM_CLASS(reason); 60 | reason() { 61 | MM_INIT(); 62 | } 63 | }; 64 | 65 | struct business : reason { 66 | MM_CLASS(business, reason); 67 | business() { 68 | MM_INIT(); 69 | } 70 | }; 71 | 72 | struct comfort : reason { 73 | MM_CLASS(comfort, reason); 74 | comfort() { 75 | MM_INIT(); 76 | } 77 | }; 78 | 79 | MULTI_METHOD(approve, bool, const virtual_&, const virtual_&, const virtual_&); 80 | 81 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const role&, const reason&) { 82 | return false; 83 | } END_SPECIALIZATION; 84 | 85 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const ceo&, const reason&) { 86 | return true; 87 | } END_SPECIALIZATION; 88 | 89 | BEGIN_SPECIALIZATION(approve, bool, const cab&, const manager&, const business&) { 90 | return true; 91 | } END_SPECIALIZATION; 92 | 93 | #define demo(exp) cout << #exp << " -> " << exp << endl 94 | 95 | int main() { 96 | yorel::multi_methods::initialize(); // IMPORTANT! 97 | cout << boolalpha; 98 | 99 | // ceo does as he pleases 100 | demo( approve(plane(), ceo(), comfort()) ); // true 101 | demo( approve(cab(), ceo(), business()) ); // true 102 | // managers only take cabs for business 103 | demo( approve(cab(), manager(), business()) ); // true 104 | demo( approve(cab(), manager(), comfort()) ); // false 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /go: -------------------------------------------------------------------------------- 1 | emacs -f magit-status include/multimethods.hpp tests/tests.cpp 2 | -------------------------------------------------------------------------------- /include/yorel/methods.hpp: -------------------------------------------------------------------------------- 1 | #ifndef YOMM11_INCLUDED 2 | #define YOMM11_INCLUDED 3 | 4 | // method.hpp 5 | // Copyright (c) 2013 Jean-Louis Leroy 6 | // Distributed under the Boost Software License, Version 1.0. (See 7 | // accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #include 11 | #include 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/yorel/methods/detail.hpp: -------------------------------------------------------------------------------- 1 | // -*- compile-command: "cd ../../.. && make && make test" -*- 2 | 3 | // method/detail.hpp 4 | // Copyright (c) 2013 Jean-Louis Leroy 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | namespace detail { 10 | 11 | // Copied from Boost. 12 | template 13 | struct is_virtual_base_of 14 | { 15 | #ifdef __BORLANDC__ 16 | struct internal_struct_X : public virtual Derived, public virtual Base 17 | { 18 | internal_struct_X(); 19 | internal_struct_X(const internal_struct_X&); 20 | internal_struct_X& operator=(const internal_struct_X&); 21 | ~internal_struct_X()throw(); 22 | }; 23 | struct internal_struct_Y : public virtual Derived 24 | { 25 | internal_struct_Y(); 26 | internal_struct_Y(const internal_struct_Y&); 27 | internal_struct_Y& operator=(const internal_struct_Y&); 28 | ~internal_struct_Y()throw(); 29 | }; 30 | #else 31 | struct internal_struct_X : public Derived, virtual Base 32 | { 33 | internal_struct_X(); 34 | internal_struct_X(const internal_struct_X&); 35 | internal_struct_X& operator=(const internal_struct_X&); 36 | ~internal_struct_X()throw(); 37 | }; 38 | struct internal_struct_Y : public Derived 39 | { 40 | internal_struct_Y(); 41 | internal_struct_Y(const internal_struct_Y&); 42 | internal_struct_Y& operator=(const internal_struct_Y&); 43 | ~internal_struct_Y()throw(); 44 | }; 45 | #endif 46 | static const int value = sizeof(internal_struct_X) == sizeof(internal_struct_Y); 47 | }; 48 | 49 | template 50 | struct cast_best; 51 | 52 | template 53 | struct cast_best : cast_using_static_cast { 54 | }; 55 | 56 | template 57 | struct cast_best : cast_using_dynamic_cast { 58 | }; 59 | 60 | template 61 | struct virtuals { 62 | }; 63 | 64 | template 65 | struct extract_virtuals { 66 | using type = typename extract_virtuals, Class...>::type; 67 | }; 68 | 69 | template 70 | struct extract_virtuals< virtuals, Next, Rest... > { 71 | using type = typename extract_virtuals< virtuals, Rest... >::type; 72 | }; 73 | 74 | template 75 | struct extract_virtuals< virtuals, virtual_&, Rest... > { 76 | using type = typename extract_virtuals< 77 | virtuals::type>, 78 | Rest... 79 | >::type; 80 | }; 81 | 82 | template 83 | struct extract_virtuals< virtuals, const virtual_&, Rest... > { 84 | using type = typename extract_virtuals< 85 | virtuals::type>, 86 | Rest... 87 | >::type; 88 | }; 89 | 90 | template 91 | struct extract_virtuals< virtuals > { 92 | using type = virtuals; 93 | }; 94 | 95 | template 96 | struct extract_method_virtuals_; 97 | 98 | template 99 | struct extract_method_virtuals { 100 | using type = typename extract_method_virtuals_, Multi, Method>::type; 101 | }; 102 | 103 | template 104 | struct extract_method_virtuals_, R1(P1, PN...), R2(A1, AN...)> { 105 | using type = typename extract_method_virtuals_< 106 | virtuals, R1(PN...), R2(AN...)>::type; 107 | }; 108 | 109 | template 110 | struct extract_method_virtuals_, R1(virtual_&, PN...), R2(A1&, AN...)> { 111 | using type = typename extract_method_virtuals_< 112 | virtuals, 113 | R1(PN...), R2(AN...) 114 | >::type; 115 | }; 116 | 117 | template 118 | struct extract_method_virtuals_, R1(const virtual_&, PN...), R2(const A1&, AN...)> { 119 | using type = typename extract_method_virtuals_< 120 | virtuals, 121 | R1(PN...), R2(AN...) 122 | >::type; 123 | }; 124 | 125 | template 126 | struct extract_method_virtuals_, R1(), R2()> { 127 | using type = virtuals; 128 | }; 129 | 130 | template 131 | struct remove_virtual { 132 | using type = T; 133 | }; 134 | 135 | template 136 | struct remove_virtual&> { 137 | using type = C&; 138 | }; 139 | 140 | template 141 | struct remove_virtual&> { 142 | using type = const C&; 143 | }; 144 | 145 | template struct yomm11_class_vector_of_; 146 | 147 | template 148 | struct yomm11_class_vector_of_ { 149 | static void get(std::vector& classes) { 150 | classes.push_back(&yomm11_class::of::the()); 151 | yomm11_class_vector_of_::get(classes); 152 | } 153 | }; 154 | 155 | template<> 156 | struct yomm11_class_vector_of_<> { 157 | static void get(std::vector& classes) { 158 | } 159 | }; 160 | 161 | template 162 | struct yomm11_class_vector_of { 163 | static std::vector get() { 164 | std::vector classes; 165 | yomm11_class_vector_of_::get(classes); 166 | return classes; 167 | } 168 | }; 169 | 170 | template 171 | struct yomm11_class_vector_of> { 172 | static std::vector get() { 173 | return yomm11_class_vector_of::get(); 174 | } 175 | }; 176 | 177 | template 178 | struct get_mm_table; 179 | 180 | template<> 181 | struct get_mm_table { 182 | template 183 | static const std::vector& value(const C* obj) { 184 | return *obj->_get_yomm11_ptbl(); 185 | } 186 | }; 187 | 188 | template 189 | struct init_ptr; 190 | 191 | template 192 | struct init_ptr> : init_ptr { }; 193 | 194 | template<> 195 | struct init_ptr> { 196 | template static void init(This* p) { 197 | p->selector::_yomm11_ptbl = &yomm11_class::of::pc->mmt; 198 | } 199 | }; 200 | 201 | template 202 | struct init_ptr { 203 | template static void init(This* p) { 204 | p->Base::_yomm11_ptbl = &yomm11_class::of::pc->mmt; 205 | init_ptr::init(p); 206 | } 207 | }; 208 | 209 | template<> 210 | struct init_ptr<> { 211 | template static void init(This* p) { 212 | } 213 | }; 214 | 215 | template 216 | struct signature { 217 | using type = SIG; 218 | }; 219 | 220 | template<> 221 | struct get_mm_table { 222 | using class_of_type = std::unordered_map*>; 223 | static class_of_type* class_of; 224 | template 225 | static const std::vector& value(const C* obj) { 226 | YOMM11_TRACE(std::cout << "foreign yomm11_class::of<" << typeid(*obj).name() << "> = " << (*class_of)[std::type_index(typeid(*obj))] << std::endl); 227 | return *(*class_of)[std::type_index(typeid(*obj))]; 228 | } 229 | }; 230 | 231 | template 232 | struct check_bases; 233 | 234 | template 235 | struct check_bases> { 236 | static const bool value = std::is_base_of::value && check_bases>::value; 237 | }; 238 | 239 | template 240 | struct check_bases> { 241 | static const bool value = true; 242 | }; 243 | 244 | template 245 | struct wrapper; 246 | 247 | template 248 | struct wrapper { 249 | using type = wrapper; 250 | static BR body(P... args) { 251 | return M::body( 252 | cast< 253 | typename std::remove_const< 254 | typename std::remove_reference

::type 255 | >::type, 256 | typename std::remove_const< 257 | typename std::remove_reference::type 258 | >::type 259 | >::value(args)...); 260 | } 261 | }; 262 | 263 | template 264 | struct wrapper { 265 | using type = M; 266 | }; 267 | 268 | template 269 | struct method_impl : specialization_base { 270 | M pm; 271 | M* pn; // next 272 | 273 | method_impl(int index, M pm, std::vector type_tuple, M* pn) : pm(pm), pn(pn) { 274 | this->index = index; 275 | this->args = type_tuple; 276 | } 277 | }; 278 | 279 | template 280 | struct throw_undefined; 281 | 282 | template 283 | struct throw_undefined { 284 | static R body(A...); 285 | }; 286 | 287 | template 288 | R throw_undefined::body(A...) { 289 | throw undefined(); 290 | } 291 | 292 | template 293 | struct throw_ambiguous; 294 | 295 | template 296 | struct throw_ambiguous { 297 | static R body(A...); 298 | }; 299 | 300 | template 301 | R throw_ambiguous::body(A...) { 302 | throw ambiguous(); 303 | } 304 | 305 | template 306 | struct method_implementation : method_base { 307 | using return_type = R; 308 | using method_pointer_type = return_type (*)(typename remove_virtual

::type...); 309 | using method_entry = method_impl; 310 | using signature = R(typename remove_virtual

::type...); 311 | using virtuals = typename extract_virtuals::type; 312 | 313 | method_implementation(YOMM11_TRACE(const char* name)) : 314 | method_base(yomm11_class_vector_of::get() YOMM11_COMMA_TRACE(name)), 315 | dispatch_table(nullptr) { 316 | } 317 | 318 | template specialization_base* add_spec(); 319 | 320 | virtual void_function_pointer* allocate_dispatch_table(int size); 321 | virtual void emit(specialization_base*, int i); 322 | virtual void emit_next(specialization_base*, specialization_base*); 323 | 324 | method_pointer_type* dispatch_table; 325 | }; 326 | 327 | template 328 | template 329 | specialization_base* method_implementation::add_spec() { 330 | using method_signature = typename M::body_signature::type; 331 | using target = typename wrapper::type; 332 | using method_virtuals = typename extract_method_virtuals::type; 333 | 334 | using namespace std; 335 | YOMM11_TRACE(cout << "add " << method_virtuals() << " to " << virtuals() << endl); 336 | 337 | specialization_base* method = new method_entry(methods.size(), target::body, yomm11_class_vector_of::get(), &M::next); 338 | methods.push_back(method); 339 | invalidate(); 340 | 341 | return method; 342 | } 343 | 344 | template 345 | method_base::void_function_pointer* method_implementation::allocate_dispatch_table(int size) { 346 | using namespace std; 347 | delete [] dispatch_table; 348 | dispatch_table = new method_pointer_type[size]; 349 | return reinterpret_cast(dispatch_table); 350 | } 351 | 352 | template 353 | void method_implementation::emit(specialization_base* method, int i) { 354 | dispatch_table[i] = 355 | method == &specialization_base::ambiguous ? throw_ambiguous::body 356 | : method == &specialization_base::undefined ? throw_undefined::body 357 | : static_cast(method)->pm; 358 | using namespace std; 359 | } 360 | 361 | template 362 | void method_implementation::emit_next(specialization_base* method, specialization_base* next) { 363 | *static_cast(method)->pn = 364 | (next == &specialization_base::ambiguous || next == &specialization_base::undefined) ? nullptr 365 | : static_cast(next)->pm; 366 | } 367 | 368 | template 369 | struct linear; 370 | 371 | template 372 | struct linear<0, P1, P...> { 373 | template 374 | static method_base::void_function_pointer* value( 375 | std::vector::const_iterator slot_iter, 376 | std::vector::const_iterator step_iter, 377 | A1, A... args) { 378 | return linear<0, P...>::value(slot_iter, step_iter, args...); 379 | } 380 | }; 381 | 382 | template 383 | struct linear<0, virtual_&, P...> { 384 | template 385 | static method_base::void_function_pointer* value( 386 | std::vector::const_iterator slot_iter, 387 | std::vector::const_iterator step_iter, 388 | A1 arg, A... args) { 389 | return linear<1, P...>::value( 390 | slot_iter + 1, step_iter + 1, 391 | detail::get_mm_table::value>::value(arg)[*slot_iter].ptr, args...); 392 | } 393 | }; 394 | 395 | template 396 | struct linear<0, const virtual_&, P...> { 397 | template 398 | static method_base::void_function_pointer* value( 399 | std::vector::const_iterator slot_iter, 400 | std::vector::const_iterator step_iter, 401 | A1 arg, A... args) { 402 | return linear<1, P...>::value( 403 | slot_iter + 1, step_iter + 1, 404 | detail::get_mm_table::value>::value(arg)[*slot_iter].ptr, args...); 405 | } 406 | }; 407 | 408 | template 409 | struct linear { 410 | template 411 | static method_base::void_function_pointer* value( 412 | std::vector::const_iterator slot_iter, 413 | std::vector::const_iterator step_iter, 414 | method_base::void_function_pointer* ptr, 415 | A1, A... args) { 416 | return linear::value(slot_iter, step_iter, ptr, args...); 417 | } 418 | }; 419 | 420 | template 421 | struct linear&, P...> { 422 | template 423 | static method_base::void_function_pointer* value( 424 | std::vector::const_iterator slot_iter, 425 | std::vector::const_iterator step_iter, 426 | method_base::void_function_pointer* ptr, 427 | A1 arg, A... args) { 428 | YOMM11_TRACE(std::cout << " -> " << ptr); 429 | return linear::value( 430 | slot_iter + 1, step_iter + 1, 431 | ptr + detail::get_mm_table::value>::value(arg)[*slot_iter].index * *step_iter, 432 | args...); 433 | } 434 | }; 435 | 436 | template 437 | struct linear&, P...> { 438 | template 439 | static method_base::void_function_pointer* value( 440 | std::vector::const_iterator slot_iter, 441 | std::vector::const_iterator step_iter, 442 | method_base::void_function_pointer* ptr, 443 | A1 arg, A... args) { 444 | YOMM11_TRACE(std::cout << " -> " << ptr); 445 | return linear::value( 446 | slot_iter + 1, step_iter + 1, 447 | ptr + detail::get_mm_table::value>::value(arg)[*slot_iter].index * *step_iter, 448 | args...); 449 | } 450 | }; 451 | 452 | template 453 | struct linear { 454 | static method_base::void_function_pointer* value( 455 | std::vector::const_iterator slot_iter, 456 | std::vector::const_iterator step_iter, 457 | method_base::void_function_pointer* ptr) { 458 | YOMM11_TRACE(std::cout << " -> " << ptr << std::endl); 459 | return ptr; 460 | } 461 | }; 462 | 463 | #ifdef YOMM11_TRACE 464 | 465 | template 466 | void write(std::ostream& os, virtuals) { 467 | os << typeid(C1).name() << ", "; 468 | write(os, virtuals()); 469 | } 470 | 471 | template 472 | void write(std::ostream& os, virtuals) { 473 | os << typeid(C).name(); 474 | } 475 | 476 | inline void write(std::ostream& os, virtuals<>) { 477 | } 478 | 479 | template 480 | std::ostream& operator <<(std::ostream& os, virtuals v){ 481 | os << "virtuals<"; 482 | write(os, v); 483 | return os << ">"; 484 | } 485 | #endif 486 | } 487 | -------------------------------------------------------------------------------- /include/yorel/methods/extern_macros.hpp: -------------------------------------------------------------------------------- 1 | // extern_macros.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #undef YOMM11_CLASS 8 | 9 | #define YOMM11_CLASS(CLASS, BASES...) \ 10 | using _yomm11_base_list = ::yorel::methods::detail::type_list; \ 11 | const char* _yomm11_class_name_(CLASS*) { return #CLASS; } \ 12 | virtual void _yomm11_init_class_() 13 | 14 | #undef YOMM11_EXTERN_CLASS 15 | 16 | #define YOMM11_EXTERN_CLASS(CLASS) \ 17 | const char* _yomm11_class_name_(CLASS*) { return #CLASS; } \ 18 | extern template class ::yorel::methods::detail::yomm11_class::of 19 | 20 | #undef YOMM11_METHOD 21 | 22 | #define YOMM11_METHOD(ID, RETURN_TYPE, ARGS...) \ 23 | template struct ID ## _specialization; \ 24 | constexpr ::yorel::methods::detail::method ID{}; \ 25 | extern template class ::yorel::methods::detail::method_implementation 26 | -------------------------------------------------------------------------------- /include/yorel/methods/macros.hpp: -------------------------------------------------------------------------------- 1 | // macros.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #undef MM_CLASS 8 | #define MM_CLASS(CLASS, ...) \ 9 | using _yomm11_base_list = ::yorel::methods::detail::type_list<__VA_ARGS__>; \ 10 | YOMM11_TRACE(friend const char* _yomm11_name_(CLASS*) { return #CLASS; }) \ 11 | virtual void _yomm11_init_class_() { &::yorel::methods::detail::yomm11_class::initializer>::the; } 12 | 13 | #undef MM_EXTERN_CLASS 14 | #define MM_EXTERN_CLASS(CLASS) 15 | 16 | #define MM_FOREIGN_CLASS(CLASS, ...) \ 17 | static_assert(::yorel::methods::detail::check_bases>::value, "error in MM_FOREIGN_CLASS(): not a base in base list"); \ 18 | static_assert(std::is_polymorphic::value, "error: class must be polymorphic"); \ 19 | YOMM11_TRACE(const char* _yomm11_name_(CLASS*) { return #CLASS; }) \ 20 | namespace { ::yorel::methods::detail::yomm11_class::initializer> _yomm11_add_class_ ## CLASS; } 21 | 22 | #define MM_INIT() \ 23 | ::yorel::methods::detail::init_ptr<_yomm11_base_list>::init(this) 24 | 25 | #undef MM_CLASS_MULTI 26 | #define MM_CLASS_MULTI(CLASS, BASE, ...) \ 27 | MM_CLASS(CLASS, BASE, __VA_ARGS__); \ 28 | std::vector* _get_yomm11_ptbl() const { return BASE::_yomm11_ptbl; } 29 | 30 | #define MM_INIT_MULTI(BASE) \ 31 | this->BASE::_init_yomm11_ptr(this) 32 | 33 | #ifdef __cpp_constexpr 34 | #define YOMM_CONSTEXPR constexpr 35 | #else 36 | #define YOMM_CONSTEXPR const 37 | #endif 38 | 39 | #undef MULTI_METHOD 40 | #define MULTI_METHOD(ID, RETURN_TYPE, ...) \ 41 | template struct ID ## _specialization; \ 42 | YOMM11_TRACE(inline const char* _yomm11_name_(::yorel::methods::detail::method*) { return #ID; }) \ 43 | YOMM_CONSTEXPR ::yorel::methods::detail::method ID 44 | 45 | #define BEGIN_SPECIALIZATION(ID, RESULT, ...) \ 46 | template<> \ 47 | struct ID ## _specialization : std::remove_const::type::specialization< ID ## _specialization > { \ 48 | virtual void* _yomm11_install() { return &::yorel::methods::detail::register_spec::the; } \ 49 | using body_signature = ::yorel::methods::detail::signature; \ 50 | static RESULT body(__VA_ARGS__) { 51 | 52 | #define END_SPECIALIZATION } }; 53 | 54 | #define GET_SPECIALIZATION(ID, RESULT, ...) ID ## _specialization::body 55 | -------------------------------------------------------------------------------- /include/yorel/methods/no_macros.hpp: -------------------------------------------------------------------------------- 1 | #ifndef YOMM11_NO_MACROS_INCLUDED 2 | #define YOMM11_NO_MACROS_INCLUDED 3 | 4 | // method/no_macros.hpp 5 | // Copyright (c) 2013 Jean-Louis Leroy 6 | // Distributed under the Boost Software License, Version 1.0. (See 7 | // accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | // Copied from Boost. 11 | #ifdef _MSVC_VER 12 | #pragma warning( push ) 13 | #pragma warning( disable: 4250 ) 14 | #pragma warning( disable: 4284 ) 15 | #pragma warning( disable: 4996 ) 16 | #elif defined(__GNUC__) && (__GNUC__ >= 4) 17 | #pragma GCC system_header 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | //#define YOMM11_ENABLE_TRACE 34 | 35 | #ifdef YOMM11_ENABLE_TRACE 36 | #define YOMM11_TRACE(e) e 37 | #define YOMM11_COMMA_TRACE(e) , e 38 | #include 39 | #else 40 | #define YOMM11_TRACE(e) 41 | #define YOMM11_COMMA_TRACE(e) 42 | #include 43 | #endif 44 | 45 | // Copied from Boost. 46 | #ifdef _MSVC_VER 47 | #pragma warning( push ) 48 | #pragma warning( disable : 4584 4250) 49 | #elif defined(__GNUC__) && (__GNUC__ >= 4) 50 | #pragma GCC system_header 51 | #endif 52 | 53 | namespace yorel { 54 | namespace methods { 55 | 56 | // Forward declarations. 57 | // All names in namespace are listed below. 58 | void initialize(); 59 | struct selector; 60 | template struct virtual_; 61 | class undefined; 62 | class ambiguous; 63 | 64 | #ifdef YOMM11_ENABLE_TRACE 65 | std::ostream& operator <<(std::ostream& os, const yomm11_class* pc); 66 | std::ostream& operator <<(std::ostream& os, const std::vector& classes); 67 | std::ostream& operator <<(std::ostream& os, specialization_base* method); 68 | std::ostream& operator <<(std::ostream& os, const std::vector& methods); 69 | std::ostream& operator <<(std::ostream& os, const method_base* pc); 70 | #endif 71 | 72 | // End of forward declarations. 73 | 74 | class undefined : public std::runtime_error { 75 | public: 76 | undefined(); 77 | 78 | protected: 79 | undefined(const std::string& message); 80 | }; 81 | 82 | class ambiguous : public undefined { 83 | public: 84 | ambiguous(); 85 | }; 86 | 87 | template struct cast_using_static_cast; 88 | template struct cast_using_dynamic_cast; 89 | template struct cast; 90 | 91 | template 92 | struct cast_using_static_cast { 93 | static D& value(B& obj) { return static_cast(obj); } 94 | static const D& value(const B& obj) { return static_cast(obj); } 95 | }; 96 | 97 | template 98 | struct cast_using_dynamic_cast { 99 | static D& value(B& obj) { return dynamic_cast(obj); } 100 | static const D& value(const B& obj) { return dynamic_cast(obj); } 101 | }; 102 | 103 | template 104 | struct cast_best; 105 | 106 | template 107 | struct cast_best : cast_using_static_cast { 108 | }; 109 | 110 | template 111 | struct cast_best : cast_using_dynamic_cast { 112 | }; 113 | 114 | namespace detail { 115 | 116 | struct yomm11_class; 117 | struct specialization_base; 118 | struct method_base; 119 | template class Method, typename Sig> struct method; 120 | 121 | //using bitvec = boost::dynamic_bitset<>; 122 | 123 | class bitvec { 124 | using word = unsigned long; 125 | public: 126 | class ref { 127 | word* p; 128 | int i; 129 | public: 130 | ref(word* p, int i) : p(p), i(i) { 131 | } 132 | operator bool() const { 133 | return (p[i / bpw] & (1 << (i % bpw))) != 0; 134 | } 135 | ref& operator =(bool val) { 136 | if (val) { 137 | p[i / bpw] |= 1 << (i % bpw); 138 | } else { 139 | p[i / bpw] &= ~(1 << (i % bpw)); 140 | } 141 | return *this; 142 | } 143 | ref& operator |=(bool val) { 144 | if (val) { 145 | p[i / bpw] |= 1 << (i % bpw); 146 | } 147 | return *this; 148 | } 149 | }; 150 | 151 | bitvec() : p(nullptr), n(0) { } 152 | 153 | bitvec(int n) : n(n), p(nullptr) { 154 | p = new word[wsize(n)]; 155 | std::fill(wbegin(), wend(), 0); 156 | } 157 | 158 | bitvec(int n, word init) : bitvec(n) { 159 | *p = init; 160 | } 161 | 162 | bitvec(const bitvec& other) : bitvec(other.n) { 163 | std::copy(other.p, other.p + wsize(other.n), p); 164 | } 165 | 166 | ~bitvec() { delete [] p; } 167 | 168 | bitvec& operator =(const bitvec& other) { 169 | size_t ws = wsize(other.n); 170 | word* np = new word[ws]; 171 | std::copy(other.wbegin(), other.wend(), np); 172 | delete [] p; 173 | p = np; 174 | n = other.size(); 175 | return *this; 176 | } 177 | 178 | int size() const { return n; } 179 | 180 | void resize(int size) { 181 | size_t new_ws = wsize(size); 182 | word* new_p = new word[new_ws]; 183 | size_t old_ws = wsize(n); 184 | auto end = std::copy(p, p + std::min(old_ws, new_ws), new_p); 185 | std::fill(end, new_p + new_ws, 0); 186 | if (size < n) { 187 | if (size_t rem = size % bpw) { 188 | end[-1] &= (1 << rem) - 1; 189 | } 190 | } 191 | delete [] p; 192 | p = new_p; 193 | n = size; 194 | } 195 | 196 | bool none() const { 197 | return std::all_of(p, p + wsize(n), [](word w) { return w == 0; }); 198 | } 199 | 200 | bool operator [](int i) const { 201 | return (p[i / bpw] & (1 << (i % bpw))) != 0; 202 | } 203 | 204 | ref operator [](int i) { return ref(p, i); } 205 | 206 | friend bitvec operator &(const bitvec& v1, const bitvec& v2) { 207 | bitvec res(v1.size()); 208 | std::transform( 209 | v1.wbegin(), v1.wend(), v2.wbegin(), res.wbegin(), 210 | [](word w1, word w2) { return w1 & w2; }); 211 | return res; 212 | } 213 | 214 | friend bool operator ==(const bitvec& v1, const bitvec& v2) { 215 | return std::equal(v1.wbegin(), v1.wend(), v2.wbegin()); 216 | } 217 | 218 | friend bool operator <(const bitvec& v1, const bitvec& v2) { 219 | return std::lexicographical_compare( 220 | v1.wbegin(), v1.wend(), 221 | v2.wbegin(), v2.wend()); 222 | } 223 | 224 | bitvec& operator |=(const bitvec& other) { 225 | std::transform( 226 | other.wbegin(), other.wend(), wbegin(), wbegin(), 227 | [](word w1, word w2) { return w1 | w2; }); 228 | return *this; 229 | } 230 | 231 | bitvec operator ~() const { 232 | bitvec res(size()); 233 | auto res_last = std::transform( 234 | wbegin(), wend(), res.wbegin(), 235 | [](word w) { return ~w; }); 236 | if (size_t rem = n % bpw) { 237 | res_last[-1] &= (1 << rem) - 1; 238 | } 239 | return res; 240 | } 241 | 242 | word* wbegin() { return p; } 243 | 244 | const word* wbegin() const { return p; } 245 | 246 | word* wend() { return p + wsize(n); } 247 | 248 | const word* wend() const { return p + wsize(n); } 249 | 250 | private: 251 | static size_t wsize(int n) { return (n + bpw - 1) / bpw; } 252 | static const int bpw = std::numeric_limits::digits; 253 | int n; 254 | word* p; 255 | }; 256 | 257 | std::ostream& operator <<(std::ostream& os, const bitvec& v); 258 | 259 | template 260 | struct type_list; 261 | 262 | struct yomm11_class { 263 | struct method_param { 264 | method_base* method; 265 | int arg; 266 | }; 267 | 268 | union offset { 269 | int index; 270 | void (**ptr)(); 271 | }; 272 | 273 | yomm11_class(YOMM11_TRACE(const char* name)); 274 | ~yomm11_class(); 275 | 276 | void initialize(const std::vector& bases); 277 | void add_method(method_base* pm, int arg); 278 | void remove_method(method_base* pm); 279 | void for_each_spec(std::function pf); 280 | void for_each_conforming(std::function pf); 281 | void for_each_conforming(std::unordered_set& visited, std::function pf); 282 | bool conforms_to(const yomm11_class& other) const; 283 | bool specializes(const yomm11_class& other) const; 284 | bool is_root() const; 285 | YOMM11_TRACE(const char* name); 286 | std::vector bases; 287 | std::vector specs; 288 | detail::bitvec mask; 289 | int index; 290 | yomm11_class* root; 291 | std::vector mmt; 292 | std::vector rooted_here; // methods rooted here for one or more args. 293 | bool abstract; 294 | 295 | static std::unordered_set* to_initialize; 296 | static void add_to_initialize(yomm11_class* pc); 297 | static void remove_from_initialize(yomm11_class* pc); 298 | 299 | template 300 | struct of { 301 | static yomm11_class* pc; 302 | static yomm11_class& the() { 303 | static yomm11_class instance YOMM11_TRACE({_yomm11_name_((Class*) nullptr)}); 304 | pc = &instance; 305 | return instance; 306 | } 307 | }; 308 | 309 | template 310 | struct of : of { }; 311 | 312 | template 313 | struct base_list; 314 | 315 | template 316 | struct initializer; 317 | 318 | template 319 | struct initializer> { 320 | initializer(); 321 | static initializer the; 322 | }; 323 | }; 324 | 325 | template 326 | yomm11_class* yomm11_class::of::pc; 327 | 328 | inline bool yomm11_class::is_root() const { 329 | return this == root; 330 | } 331 | 332 | struct specialization_base { 333 | virtual ~specialization_base(); 334 | 335 | int index; // inside method 336 | std::vector args; 337 | bool specializes(specialization_base* other) const; 338 | static specialization_base undefined; 339 | static specialization_base ambiguous; 340 | }; 341 | 342 | struct method_base { 343 | explicit method_base(const std::vector& v YOMM11_COMMA_TRACE(const char* name)); 344 | virtual ~method_base(); 345 | 346 | using void_function_pointer = void (*)(); 347 | 348 | void resolve(); 349 | virtual void_function_pointer* allocate_dispatch_table(int size) = 0; 350 | virtual void emit(specialization_base*, int i) = 0; 351 | virtual void emit_next(specialization_base*, specialization_base*) = 0; 352 | void invalidate(); 353 | void assign_slot(int arg, int slot); 354 | 355 | std::vector vargs; 356 | std::vector slots; 357 | std::vector methods; 358 | std::vector steps; 359 | YOMM11_TRACE(const char* name); 360 | 361 | static std::unordered_set* to_initialize; 362 | static void add_to_initialize(method_base* pm); 363 | static void remove_from_initialize(method_base* pm); 364 | }; 365 | 366 | // Copied from Boost. 367 | template 368 | struct is_virtual_base_of 369 | { 370 | #ifdef __BORLANDC__ 371 | struct internal_struct_X : public virtual Derived, public virtual Base 372 | { 373 | internal_struct_X(); 374 | internal_struct_X(const internal_struct_X&); 375 | internal_struct_X& operator=(const internal_struct_X&); 376 | ~internal_struct_X()throw(); 377 | virtual void _yomm11_init_class_() { } 378 | }; 379 | struct internal_struct_Y : public virtual Derived 380 | { 381 | internal_struct_Y(); 382 | internal_struct_Y(const internal_struct_Y&); 383 | internal_struct_Y& operator=(const internal_struct_Y&); 384 | ~internal_struct_Y()throw(); 385 | virtual void _yomm11_init_class_() { } 386 | }; 387 | #else 388 | struct internal_struct_X : public Derived, virtual Base 389 | { 390 | internal_struct_X(); 391 | internal_struct_X(const internal_struct_X&); 392 | internal_struct_X& operator=(const internal_struct_X&); 393 | ~internal_struct_X()throw(); 394 | virtual void _yomm11_init_class_() { } 395 | }; 396 | struct internal_struct_Y : public Derived 397 | { 398 | internal_struct_Y(); 399 | internal_struct_Y(const internal_struct_Y&); 400 | internal_struct_Y& operator=(const internal_struct_Y&); 401 | ~internal_struct_Y()throw(); 402 | virtual void _yomm11_init_class_() { } 403 | }; 404 | #endif 405 | static const int value = sizeof(internal_struct_X) == sizeof(internal_struct_Y); 406 | }; 407 | 408 | template 409 | struct virtuals { 410 | }; 411 | 412 | template 413 | struct extract_virtuals { 414 | using type = typename extract_virtuals, Class...>::type; 415 | }; 416 | 417 | template 418 | struct extract_virtuals< virtuals, Next, Rest... > { 419 | using type = typename extract_virtuals< virtuals, Rest... >::type; 420 | }; 421 | 422 | template 423 | struct extract_virtuals< virtuals, virtual_&, Rest... > { 424 | using type = typename extract_virtuals< 425 | virtuals::type>, 426 | Rest... 427 | >::type; 428 | }; 429 | 430 | template 431 | struct extract_virtuals< virtuals, const virtual_&, Rest... > { 432 | using type = typename extract_virtuals< 433 | virtuals::type>, 434 | Rest... 435 | >::type; 436 | }; 437 | 438 | template 439 | struct extract_virtuals< virtuals > { 440 | using type = virtuals; 441 | }; 442 | 443 | template 444 | struct extract_method_virtuals_; 445 | 446 | template 447 | struct extract_method_virtuals { 448 | using type = typename extract_method_virtuals_, Multi, Method>::type; 449 | }; 450 | 451 | template 452 | struct extract_method_virtuals_, R1(P1, PN...), R2(A1, AN...)> { 453 | using type = typename extract_method_virtuals_< 454 | virtuals, R1(PN...), R2(AN...)>::type; 455 | }; 456 | 457 | template 458 | struct extract_method_virtuals_, R1(virtual_&, PN...), R2(A1&, AN...)> { 459 | using type = typename extract_method_virtuals_< 460 | virtuals, 461 | R1(PN...), R2(AN...) 462 | >::type; 463 | }; 464 | 465 | template 466 | struct extract_method_virtuals_, R1(const virtual_&, PN...), R2(const A1&, AN...)> { 467 | using type = typename extract_method_virtuals_< 468 | virtuals, 469 | R1(PN...), R2(AN...) 470 | >::type; 471 | }; 472 | 473 | template 474 | struct extract_method_virtuals_, R1(), R2()> { 475 | using type = virtuals; 476 | }; 477 | 478 | template 479 | struct remove_virtual { 480 | using type = T; 481 | }; 482 | 483 | template 484 | struct remove_virtual&> { 485 | using type = C&; 486 | }; 487 | 488 | template 489 | struct remove_virtual&> { 490 | using type = const C&; 491 | }; 492 | 493 | template struct yomm11_class_vector_of_; 494 | 495 | template 496 | struct yomm11_class_vector_of_ { 497 | static void get(std::vector& classes) { 498 | classes.push_back(&yomm11_class::of::the()); 499 | yomm11_class_vector_of_::get(classes); 500 | } 501 | }; 502 | 503 | template<> 504 | struct yomm11_class_vector_of_<> { 505 | static void get(std::vector& classes) { 506 | } 507 | }; 508 | 509 | template 510 | struct yomm11_class_vector_of { 511 | static std::vector get() { 512 | std::vector classes; 513 | yomm11_class_vector_of_::get(classes); 514 | return classes; 515 | } 516 | }; 517 | 518 | template 519 | struct yomm11_class_vector_of> { 520 | static std::vector get() { 521 | return yomm11_class_vector_of::get(); 522 | } 523 | }; 524 | 525 | template 526 | struct get_mm_table; 527 | 528 | template<> 529 | struct get_mm_table { 530 | template 531 | static const std::vector& value(const C* obj) { 532 | return *obj->_get_yomm11_ptbl(); 533 | } 534 | }; 535 | 536 | template 537 | struct init_ptr; 538 | 539 | template 540 | struct init_ptr> : init_ptr { }; 541 | 542 | template<> 543 | struct init_ptr> { 544 | template static void init(This* p) { 545 | p->selector::_yomm11_ptbl = &yomm11_class::of::pc->mmt; 546 | } 547 | }; 548 | 549 | template 550 | struct init_ptr { 551 | template static void init(This* p) { 552 | p->Base::_yomm11_ptbl = &yomm11_class::of::pc->mmt; 553 | init_ptr::init(p); 554 | } 555 | }; 556 | 557 | template<> 558 | struct init_ptr<> { 559 | template static void init(This* p) { 560 | } 561 | }; 562 | 563 | template 564 | struct signature { 565 | using type = SIG; 566 | }; 567 | 568 | template<> 569 | struct get_mm_table { 570 | using class_of_type = std::unordered_map*>; 571 | static class_of_type* class_of; 572 | template 573 | static const std::vector& value(const C* obj) { 574 | YOMM11_TRACE(std::cout << "foreign yomm11_class::of<" << typeid(*obj).name() << "> = " << (*class_of)[std::type_index(typeid(*obj))] << std::endl); 575 | return *(*class_of)[std::type_index(typeid(*obj))]; 576 | } 577 | }; 578 | 579 | template 580 | struct check_bases; 581 | 582 | template 583 | struct check_bases> { 584 | static const bool value = std::is_base_of::value && check_bases>::value; 585 | }; 586 | 587 | template 588 | struct check_bases> { 589 | static const bool value = true; 590 | }; 591 | 592 | template 593 | struct has_nonvirtual_bases; 594 | 595 | template 596 | struct has_nonvirtual_bases> : has_nonvirtual_bases{}; 597 | 598 | template 599 | struct has_nonvirtual_bases { 600 | static const int value = !is_virtual_base_of::value 601 | || has_nonvirtual_bases::value; 602 | }; 603 | 604 | template 605 | struct has_nonvirtual_bases { 606 | static const int value = false; 607 | }; 608 | 609 | template 610 | struct wrapper; 611 | 612 | template 613 | struct wrapper { 614 | using type = wrapper; 615 | static BR body(P... args) { 616 | return M::body( 617 | cast< 618 | typename std::remove_const< 619 | typename std::remove_reference

::type 620 | >::type, 621 | typename std::remove_const< 622 | typename std::remove_reference::type 623 | >::type 624 | >::value(args)...); 625 | } 626 | }; 627 | 628 | template 629 | struct wrapper { 630 | using type = M; 631 | }; 632 | 633 | template 634 | struct method_impl : specialization_base { 635 | M pm; 636 | M* pn; // next 637 | 638 | method_impl(int index, M pm, std::vector type_tuple, M* pn) : pm(pm), pn(pn) { 639 | this->index = index; 640 | this->args = type_tuple; 641 | } 642 | }; 643 | 644 | template 645 | struct throw_undefined; 646 | 647 | template 648 | struct throw_undefined { 649 | static R body(A...); 650 | }; 651 | 652 | template 653 | R throw_undefined::body(A...) { 654 | throw undefined(); 655 | } 656 | 657 | template 658 | struct throw_ambiguous; 659 | 660 | template 661 | struct throw_ambiguous { 662 | static R body(A...); 663 | }; 664 | 665 | template 666 | R throw_ambiguous::body(A...) { 667 | throw ambiguous(); 668 | } 669 | 670 | template 671 | struct method_implementation : method_base { 672 | using return_type = R; 673 | using method_pointer_type = return_type (*)(typename remove_virtual

::type...); 674 | using method_entry = method_impl; 675 | using signature = R(typename remove_virtual

::type...); 676 | using virtuals = typename extract_virtuals::type; 677 | 678 | method_implementation(YOMM11_TRACE(const char* name)) : 679 | method_base(yomm11_class_vector_of::get() YOMM11_COMMA_TRACE(name)), 680 | dispatch_table(nullptr) { 681 | } 682 | 683 | template specialization_base* add_spec(); 684 | 685 | virtual void_function_pointer* allocate_dispatch_table(int size); 686 | virtual void emit(specialization_base*, int i); 687 | virtual void emit_next(specialization_base*, specialization_base*); 688 | 689 | method_pointer_type* dispatch_table; 690 | }; 691 | 692 | template 693 | template 694 | specialization_base* method_implementation::add_spec() { 695 | using method_signature = typename M::body_signature::type; 696 | using target = typename wrapper::type; 697 | using method_virtuals = typename extract_method_virtuals::type; 698 | 699 | using namespace std; 700 | YOMM11_TRACE(cout << "add " << method_virtuals() << " to " << virtuals() << endl); 701 | 702 | specialization_base* method = new method_entry(methods.size(), target::body, yomm11_class_vector_of::get(), &M::next); 703 | methods.push_back(method); 704 | invalidate(); 705 | 706 | return method; 707 | } 708 | 709 | template 710 | method_base::void_function_pointer* method_implementation::allocate_dispatch_table(int size) { 711 | using namespace std; 712 | delete [] dispatch_table; 713 | dispatch_table = new method_pointer_type[size]; 714 | return reinterpret_cast(dispatch_table); 715 | } 716 | 717 | template 718 | void method_implementation::emit(specialization_base* method, int i) { 719 | dispatch_table[i] = 720 | method == &specialization_base::ambiguous ? throw_ambiguous::body 721 | : method == &specialization_base::undefined ? throw_undefined::body 722 | : static_cast(method)->pm; 723 | using namespace std; 724 | YOMM11_TRACE(cout << "installed at " << dispatch_table << " + " << i << endl); 725 | } 726 | 727 | template 728 | void method_implementation::emit_next(specialization_base* method, specialization_base* next) { 729 | *static_cast(method)->pn = 730 | (next == &specialization_base::ambiguous || next == &specialization_base::undefined) ? nullptr 731 | : static_cast(next)->pm; 732 | } 733 | 734 | template 735 | struct linear; 736 | 737 | template 738 | struct linear<0, P1, P...> { 739 | template 740 | static method_base::void_function_pointer* value( 741 | std::vector::const_iterator slot_iter, 742 | std::vector::const_iterator step_iter, 743 | A1, A... args) { 744 | return linear<0, P...>::value(slot_iter, step_iter, args...); 745 | } 746 | }; 747 | 748 | template 749 | struct linear<0, virtual_&, P...> { 750 | template 751 | static method_base::void_function_pointer* value( 752 | std::vector::const_iterator slot_iter, 753 | std::vector::const_iterator step_iter, 754 | A1 arg, A... args) { 755 | return linear<1, P...>::value( 756 | slot_iter + 1, step_iter + 1, 757 | detail::get_mm_table::value>::value(arg)[*slot_iter].ptr, args...); 758 | } 759 | }; 760 | 761 | template 762 | struct linear<0, const virtual_&, P...> { 763 | template 764 | static method_base::void_function_pointer* value( 765 | std::vector::const_iterator slot_iter, 766 | std::vector::const_iterator step_iter, 767 | A1 arg, A... args) { 768 | return linear<1, P...>::value( 769 | slot_iter + 1, step_iter + 1, 770 | detail::get_mm_table::value>::value(arg)[*slot_iter].ptr, args...); 771 | } 772 | }; 773 | 774 | template 775 | struct linear { 776 | template 777 | static method_base::void_function_pointer* value( 778 | std::vector::const_iterator slot_iter, 779 | std::vector::const_iterator step_iter, 780 | method_base::void_function_pointer* ptr, 781 | A1, A... args) { 782 | return linear::value(slot_iter, step_iter, ptr, args...); 783 | } 784 | }; 785 | 786 | template 787 | struct linear&, P...> { 788 | template 789 | static method_base::void_function_pointer* value( 790 | std::vector::const_iterator slot_iter, 791 | std::vector::const_iterator step_iter, 792 | method_base::void_function_pointer* ptr, 793 | A1 arg, A... args) { 794 | YOMM11_TRACE(std::cout << " -> " << ptr); 795 | return linear::value( 796 | slot_iter + 1, step_iter + 1, 797 | ptr + detail::get_mm_table::value>::value(arg)[*slot_iter].index * *step_iter, 798 | args...); 799 | } 800 | }; 801 | 802 | template 803 | struct linear&, P...> { 804 | template 805 | static method_base::void_function_pointer* value( 806 | std::vector::const_iterator slot_iter, 807 | std::vector::const_iterator step_iter, 808 | method_base::void_function_pointer* ptr, 809 | A1 arg, A... args) { 810 | YOMM11_TRACE(std::cout << " -> " << ptr); 811 | return linear::value( 812 | slot_iter + 1, step_iter + 1, 813 | ptr + detail::get_mm_table::value>::value(arg)[*slot_iter].index * *step_iter, 814 | args...); 815 | } 816 | }; 817 | 818 | template 819 | struct linear { 820 | static method_base::void_function_pointer* value( 821 | std::vector::const_iterator slot_iter, 822 | std::vector::const_iterator step_iter, 823 | method_base::void_function_pointer* ptr) { 824 | YOMM11_TRACE(std::cout << " -> " << ptr << std::endl); 825 | return ptr; 826 | } 827 | }; 828 | 829 | #ifdef YOMM11_TRACE 830 | 831 | template 832 | void write(std::ostream& os, virtuals) { 833 | os << typeid(C1).name() << ", "; 834 | write(os, virtuals()); 835 | } 836 | 837 | template 838 | void write(std::ostream& os, virtuals) { 839 | os << typeid(C).name(); 840 | } 841 | 842 | inline void write(std::ostream& os, virtuals<>) { 843 | } 844 | 845 | template 846 | std::ostream& operator <<(std::ostream& os, virtuals v){ 847 | os << "virtuals<"; 848 | write(os, v); 849 | return os << ">"; 850 | } 851 | #endif 852 | 853 | template 854 | yomm11_class::initializer>::initializer() { 855 | static_assert( 856 | detail::check_bases>::value, 857 | "Error in YOMM11_CLASS(): not a base in base list"); 858 | yomm11_class& pc = yomm11_class::of::the(); 859 | pc.abstract = std::is_abstract::value; 860 | pc.initialize(detail::yomm11_class_vector_of::get()); 861 | 862 | if (!std::is_base_of::value) { 863 | if (!detail::get_mm_table::class_of) { 864 | detail::get_mm_table::class_of = new detail::get_mm_table::class_of_type; 865 | } 866 | (*detail::get_mm_table::class_of)[std::type_index(typeid(Class))] = &pc.mmt; 867 | } 868 | } 869 | 870 | template 871 | yomm11_class::initializer> yomm11_class::initializer>::the; 872 | 873 | template class Method, typename R, typename... P> 874 | struct method { 875 | 876 | #ifdef __cpp_constexpr 877 | constexpr 878 | #endif 879 | method() {} 880 | R operator ()(typename detail::remove_virtual

::type... args) const; 881 | static R resolve(typename detail::remove_virtual

::type... args); 882 | 883 | using return_type = R; 884 | using method_pointer_type = return_type (*)(typename detail::remove_virtual

::type...); 885 | using method_entry = detail::method_impl; 886 | using signature = R(typename detail::remove_virtual

::type...); 887 | using virtuals = typename detail::extract_virtuals::type; 888 | 889 | template 890 | struct next_ptr { 891 | static method_pointer_type next; 892 | }; 893 | 894 | method_pointer_type next_ptr_type() const; 895 | 896 | using implementation = detail::method_implementation; 897 | static implementation& the(); 898 | static implementation* impl; 899 | 900 | template 901 | static bool specialize() { 902 | the().template add_spec(); 903 | return true; 904 | } 905 | 906 | template 907 | struct specialization { 908 | static method_pointer_type next; 909 | // this doesn't work on clang, must do it in YOMM11_SPECIALIZATION 910 | // virtual void* _yomm11_install() { return ®ister_spec::the; } 911 | }; 912 | }; 913 | 914 | template 915 | struct register_spec { 916 | register_spec() { 917 | Method::template specialize(); 918 | } 919 | static register_spec the; 920 | }; 921 | 922 | template 923 | register_spec register_spec::the; 924 | 925 | template class Method, typename R, typename... P> 926 | template 927 | typename method::method_pointer_type 928 | method::specialization::next; 929 | 930 | template class Method, typename R, typename... P> 931 | typename method::implementation* method::impl; 932 | 933 | template class Method, typename R, typename... P> 934 | template 935 | typename method::method_pointer_type method::next_ptr::next; 936 | 937 | template class Method, typename R, typename... P> 938 | typename method::implementation& method::the() { 939 | if (!impl) { 940 | impl = new implementation(YOMM11_TRACE(_yomm11_name_((method*) nullptr))); 941 | } 942 | 943 | return *impl; 944 | } 945 | 946 | template class Method, typename R, typename... P> 947 | inline R method::operator ()(typename detail::remove_virtual

::type... args) const { 948 | YOMM11_TRACE((std::cout << "() mm table = " << impl->dispatch_table << std::flush)); 949 | return reinterpret_cast(*detail::linear<0, P...>::value(impl->slots.begin(), impl->steps.begin(), &args...))(args...); 950 | } 951 | 952 | template class Method, typename R, typename... P> 953 | inline R method::resolve(typename detail::remove_virtual

::type... args) { 954 | YOMM11_TRACE((std::cout << "() mm table = " << impl->dispatch_table << std::flush)); 955 | return reinterpret_cast(*detail::linear<0, P...>::value(impl->slots.begin(), impl->steps.begin(), &args...))(args...); 956 | } 957 | 958 | } // detail 959 | 960 | template 961 | struct cast : cast_best::value> { 962 | }; 963 | 964 | template 965 | struct cast { 966 | static B& value(B& obj) { return obj; } 967 | static const B& value(const B& obj) { return obj; } 968 | }; 969 | 970 | struct selector { 971 | selector() : _yomm11_ptbl(0) { } 972 | std::vector* _yomm11_ptbl; 973 | virtual ~selector() { } 974 | template 975 | void _init_yomm11_ptr(THIS*); 976 | std::vector* _get_yomm11_ptbl() const { return _yomm11_ptbl; } 977 | }; 978 | 979 | template 980 | inline void selector::_init_yomm11_ptr(THIS*) { 981 | _yomm11_ptbl = &detail::yomm11_class::of::the().mmt; 982 | } 983 | 984 | template 985 | struct virtual_ { 986 | using type = Class; 987 | }; 988 | 989 | } // methods 990 | namespace multi_methods = methods; 991 | } // yorel 992 | 993 | #ifdef _MSVC_VER 994 | #pragma warning( pop ) 995 | #endif 996 | #endif 997 | -------------------------------------------------------------------------------- /include/yorel/methods/runtime.hpp: -------------------------------------------------------------------------------- 1 | #ifndef YOMM11_RUNTIME_INCLUDED 2 | #define YOMM11_RUNTIME_INCLUDED 3 | 4 | // method/runtime.hpp 5 | // Copyright (c) 2013 Jean-Louis Leroy 6 | // Distributed under the Boost Software License, Version 1.0. (See 7 | // accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | namespace yorel { 11 | namespace methods { 12 | namespace detail { 13 | 14 | struct hierarchy_initializer { 15 | hierarchy_initializer(yomm11_class& root); 16 | 17 | void collect_classes(); 18 | void make_masks(); 19 | void assign_slots(); 20 | void execute(); 21 | 22 | static void initialize(yomm11_class& root); 23 | 24 | void topological_sort_visit(std::unordered_set& once, yomm11_class* pc); 25 | 26 | yomm11_class& root; 27 | std::vector nodes; 28 | }; 29 | 30 | struct grouping_resolver { 31 | grouping_resolver(method_base& mm); 32 | 33 | void resolve(); 34 | void resolve(int dim, const bitvec& candidates); 35 | void find_applicable(int dim, const yomm11_class* pc, std::vector& best); 36 | specialization_base* find_best(const bitvec& candidates); 37 | specialization_base* find_best(const std::vector& methods); 38 | void make_mask(const std::vector& best, bitvec& mask); 39 | void make_groups(); 40 | void make_table(); 41 | void assign_next(); 42 | 43 | struct group { 44 | bitvec mask; 45 | std::vector methods; 46 | std::vector classes; 47 | }; 48 | 49 | method_base& mm; 50 | const int dims; 51 | std::vector> groups; 52 | method_base::void_function_pointer* dispatch_table; 53 | int emit_at; 54 | }; 55 | } 56 | } 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /include/yorel/multi_methods.hpp: -------------------------------------------------------------------------------- 1 | // multi_method.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | -------------------------------------------------------------------------------- /include/yorel/multi_methods/extern_macros.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /include/yorel/multi_methods/macros.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt 2 | # Copyright (c) 2013 Jean-Louis Leroy 3 | # Distributed under the Boost Software License, Version 1.0. (See 4 | # accompanying file LICENSE_1_0.txt or copy at 5 | # http:#www.boost.org/LICENSE_1_0.txt) 6 | 7 | add_library(yomm11 yomm11.cpp) 8 | 9 | INSTALL(TARGETS yomm11 10 | DESTINATION lib 11 | ) 12 | -------------------------------------------------------------------------------- /src/yomm11.cpp: -------------------------------------------------------------------------------- 1 | // method.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | namespace yorel { 18 | namespace methods { 19 | 20 | using namespace detail; 21 | 22 | undefined::undefined() : 23 | runtime_error("multi-method call is undefined for these arguments") { 24 | } 25 | 26 | undefined::undefined(const std::string& message) : runtime_error(message) { 27 | } 28 | 29 | ambiguous::ambiguous() : 30 | undefined("multi-method call is ambiguous for these arguments") { 31 | } 32 | 33 | using class_set = std::unordered_set; 34 | 35 | yomm11_class::yomm11_class(YOMM11_TRACE(const char* name)) : abstract(false), index(-1), root(nullptr) YOMM11_COMMA_TRACE(name(name)) { 36 | } 37 | 38 | yomm11_class::~yomm11_class() { 39 | for (yomm11_class* base : bases) { 40 | base->specs.erase( 41 | remove_if(base->specs.begin(), base->specs.end(), [=](yomm11_class* pc) { 42 | return pc == this; 43 | }), 44 | base->specs.end()); 45 | } 46 | 47 | add_to_initialize(root); 48 | } 49 | 50 | void yomm11_class::for_each_spec(function pf) { 51 | for_each(specs.begin(), specs.end(), 52 | [=](yomm11_class* p) { p->for_each_conforming(pf); }); 53 | } 54 | 55 | void yomm11_class::for_each_conforming(unordered_set& visited, function pf) { 56 | if (visited.find(this) == visited.end()) { 57 | pf(this); 58 | visited.insert(this); 59 | for_each( 60 | specs.begin(), specs.end(), 61 | [=](yomm11_class* p) { p->for_each_conforming(pf); }); 62 | } 63 | } 64 | 65 | void yomm11_class::for_each_conforming(function pf) { 66 | pf(this); 67 | for_each(specs.begin(), specs.end(), 68 | [=](yomm11_class* p) { p->for_each_conforming(pf); }); 69 | } 70 | 71 | bool yomm11_class::conforms_to(const yomm11_class& other) const { 72 | return index >= other.index && other.mask[index]; 73 | } 74 | 75 | bool yomm11_class::specializes(const yomm11_class& other) const { 76 | return index > other.index && other.mask[index]; 77 | } 78 | 79 | void yomm11_class::initialize(const vector& b) { 80 | YOMM11_TRACE(cout << "initialize class_of<" << name << ">\n"); 81 | 82 | if (root) { 83 | throw runtime_error("methods: class redefinition"); 84 | } 85 | 86 | bases = b; 87 | 88 | if (bases.empty()) { 89 | root = this; 90 | } else { 91 | // if (any_of(bases.begin(), bases.end(), [&](const yomm11_class* base) { 92 | // return base->root != bases.front()->root; 93 | // })) { 94 | // throw runtime_error("hierarchy must have a single root"); 95 | // } 96 | root = bases[0]->root; 97 | } 98 | 99 | for (yomm11_class* pb : bases) { 100 | pb->specs.push_back(this); 101 | } 102 | 103 | add_to_initialize(root); 104 | 105 | root->for_each_conforming([](yomm11_class* pc) { 106 | for (auto& mr : pc->rooted_here) { 107 | mr.method->invalidate(); 108 | } 109 | }); 110 | } 111 | 112 | unordered_set* yomm11_class::to_initialize; 113 | 114 | void yomm11_class::add_to_initialize(yomm11_class* pc) { 115 | YOMM11_TRACE(cout << "add to initialize: " << pc << endl); 116 | 117 | if (!to_initialize) { 118 | to_initialize = new unordered_set; 119 | } 120 | 121 | to_initialize->insert(pc); 122 | } 123 | 124 | void yomm11_class::remove_from_initialize(yomm11_class* pc) { 125 | YOMM11_TRACE(cout << "remove from initialize: " << pc->name << endl); 126 | 127 | if (to_initialize) { 128 | to_initialize->erase(pc); 129 | 130 | if (to_initialize->empty()) { 131 | delete to_initialize; 132 | to_initialize = nullptr; 133 | } 134 | } 135 | } 136 | 137 | specialization_base specialization_base::undefined; 138 | specialization_base specialization_base::ambiguous; 139 | 140 | hierarchy_initializer::hierarchy_initializer(yomm11_class& root) : root(root) { 141 | } 142 | 143 | void hierarchy_initializer::initialize(yomm11_class& root) { 144 | hierarchy_initializer init(root); 145 | init.execute(); 146 | } 147 | 148 | void hierarchy_initializer::topological_sort_visit(std::unordered_set& once, yomm11_class* pc) { 149 | if (once.find(pc) == once.end()) { 150 | once.insert(pc); 151 | 152 | for (yomm11_class* base : pc->bases) { 153 | topological_sort_visit(once, base); 154 | } 155 | 156 | nodes.push_back(pc); 157 | } 158 | } 159 | 160 | void hierarchy_initializer::execute() { 161 | YOMM11_TRACE(cout << "assigning slots for hierarchy rooted in " << &root << endl); 162 | collect_classes(); 163 | make_masks(); 164 | assign_slots(); 165 | 166 | for (auto pc : nodes) { 167 | if (pc->is_root()) { 168 | yomm11_class::remove_from_initialize(pc); 169 | } 170 | } 171 | } 172 | 173 | void hierarchy_initializer::collect_classes() { 174 | std::unordered_set once; 175 | root.for_each_conforming([&](yomm11_class* pc) { 176 | topological_sort_visit(once, pc); 177 | }); 178 | } 179 | 180 | void hierarchy_initializer::make_masks() { 181 | int mark = 0; 182 | const int nb = nodes.size(); 183 | 184 | for (auto pc : nodes) { 185 | pc->mask.resize(nb); 186 | pc->index = mark++; 187 | pc->mask[pc->index] = true; 188 | } 189 | 190 | for (auto pc_iter = nodes.rbegin(); pc_iter != nodes.rend(); pc_iter++) { 191 | for (yomm11_class* spec : (*pc_iter)->specs) { 192 | for (int bit = spec->index; bit < nb; bit++) { 193 | (*pc_iter)->mask[bit] |= nodes[spec->index]->mask[bit]; 194 | } 195 | } 196 | } 197 | } 198 | 199 | void hierarchy_initializer::assign_slots() { 200 | vector slots; 201 | 202 | for (auto pc : nodes) { 203 | int max_slots = 0; 204 | 205 | for (auto& mm : pc->rooted_here) { 206 | auto available_slot = find_if( 207 | slots.begin(), slots.end(), 208 | [=](const bitvec& mask) { 209 | return (mask & pc->mask).none(); 210 | }); 211 | 212 | if (available_slot == slots.end()) { 213 | slots.push_back(bitvec(nodes.size())); 214 | available_slot = slots.end() - 1; 215 | } 216 | 217 | *available_slot |= pc->mask; 218 | 219 | int slot = available_slot - slots.begin(); 220 | max_slots = max(max_slots, slot + 1); 221 | 222 | YOMM11_TRACE(cout << "slot " << slot << " -> " << mm.method->vargs 223 | << " (arg " << mm.arg << ")" << endl); 224 | mm.method->assign_slot(mm.arg, slot); 225 | } 226 | 227 | int max_inherited_slots = pc->bases.empty() ? 0 228 | : (*max_element( 229 | pc->bases.begin(), pc->bases.end(), 230 | [](const yomm11_class* b1, const yomm11_class* b2) { return b1->mmt.size() < b2->mmt.size(); }))->mmt.size(); 231 | 232 | YOMM11_TRACE(cout << pc << ":max inherited slots: " << max_inherited_slots 233 | << ", max direct slots: " << max_slots << endl); 234 | pc->mmt.resize(max(max_inherited_slots, max_slots)); 235 | } 236 | } 237 | 238 | void initialize() { 239 | while (yomm11_class::to_initialize) { 240 | auto pc = *yomm11_class::to_initialize->begin(); 241 | if (pc->is_root()) { 242 | hierarchy_initializer::initialize(*pc); 243 | } else { 244 | yomm11_class::remove_from_initialize(pc); 245 | } 246 | } 247 | 248 | while (method_base::to_initialize) { 249 | auto pm = *method_base::to_initialize->begin(); 250 | pm->resolve(); 251 | method_base::remove_from_initialize(pm); 252 | } 253 | } 254 | 255 | specialization_base::~specialization_base() { 256 | } 257 | 258 | bool specialization_base::specializes(specialization_base* other) const { 259 | 260 | if (this == other) { 261 | return false; 262 | } 263 | 264 | bool result = false; 265 | 266 | for (size_t dim = 0; dim < args.size(); dim++) { 267 | if (args[dim] != other->args[dim]) { 268 | if (args[dim]->specializes(*other->args[dim])) { 269 | result = true; 270 | } else { 271 | return false; 272 | } 273 | } 274 | } 275 | 276 | return result; 277 | } 278 | 279 | void yomm11_class::add_method(method_base* pm, int arg) { 280 | rooted_here.push_back(method_param { pm, arg }); 281 | add_to_initialize(this); 282 | } 283 | 284 | void yomm11_class::remove_method(method_base* pm) { 285 | rooted_here.erase( 286 | remove_if(rooted_here.begin(), rooted_here.end(), [=](method_param& ref) { return ref.method == pm; }), 287 | rooted_here.end()); 288 | add_to_initialize(root); 289 | } 290 | 291 | get_mm_table::class_of_type* get_mm_table::class_of; 292 | 293 | ostream& operator <<(ostream& os, const vector& classes) { 294 | using namespace std; 295 | const char* sep = "("; 296 | 297 | for (yomm11_class* pc : classes) { 298 | os << sep; 299 | sep = ", "; 300 | os << pc; 301 | } 302 | 303 | return os << ")"; 304 | } 305 | 306 | method_base::method_base(const vector& v YOMM11_COMMA_TRACE(const char* name)) 307 | : vargs(v) YOMM11_COMMA_TRACE(name(name)) { 308 | int i = 0; 309 | for (auto pc : vargs) { 310 | YOMM11_TRACE(cout << "add " << name << " rooted in " << pc->name << " argument " << i << "\n"); 311 | pc->add_method(this, i++); 312 | } 313 | slots.resize(v.size()); 314 | } 315 | 316 | method_base::~method_base() { 317 | for (auto method_iter = methods.rbegin(); method_iter != methods.rend(); method_iter++) { 318 | delete *method_iter; 319 | *method_iter = 0; 320 | } 321 | 322 | for (yomm11_class* arg : vargs) { 323 | arg->remove_method(this); 324 | } 325 | 326 | remove_from_initialize(this); 327 | } 328 | 329 | void method_base::assign_slot(int arg, int slot) { 330 | slots[arg] = slot; 331 | invalidate(); 332 | } 333 | 334 | void method_base::invalidate() { 335 | YOMM11_TRACE(cout << "add " << name << " to init list" << endl); 336 | add_to_initialize(this); 337 | } 338 | 339 | unordered_set* method_base::to_initialize; 340 | 341 | void method_base::add_to_initialize(method_base* pm) { 342 | if (!to_initialize) { 343 | to_initialize = new unordered_set; 344 | } 345 | 346 | to_initialize->insert(pm); 347 | } 348 | 349 | void method_base::remove_from_initialize(method_base* pm) { 350 | if (to_initialize) { 351 | to_initialize->erase(pm); 352 | 353 | if (to_initialize->empty()) { 354 | delete to_initialize; 355 | to_initialize = nullptr; 356 | } 357 | } 358 | } 359 | 360 | void method_base::resolve() { 361 | grouping_resolver r(*this); 362 | r.resolve(); 363 | } 364 | 365 | grouping_resolver::grouping_resolver(method_base& mm) : mm(mm), dims(mm.vargs.size()) { 366 | } 367 | 368 | void grouping_resolver::resolve() { 369 | make_groups(); 370 | make_table(); 371 | assign_next(); 372 | } 373 | 374 | void grouping_resolver::make_groups() { 375 | groups.resize(dims); 376 | 377 | int dim = 0; 378 | mm.steps.resize(dims); 379 | int step = 1; 380 | 381 | for (auto& dim_groups : groups) { 382 | YOMM11_TRACE(cout << "make_groups dim = " << dim << endl); 383 | 384 | unordered_set once; 385 | mm.steps[dim] = step; 386 | 387 | mm.vargs[dim]->for_each_conforming(once, [&](yomm11_class* pc) { 388 | group g; 389 | find_applicable(dim, pc, g.methods); 390 | g.classes.push_back(pc); 391 | make_mask(g.methods, g.mask); 392 | YOMM11_TRACE(cout << pc << " has " << g.methods << endl); 393 | auto lower = lower_bound( 394 | dim_groups.begin(), dim_groups.end(), g, 395 | []( const group& g1, const group& g2) { return g1.mask < g2.mask; }); 396 | 397 | if (lower == dim_groups.end() || g.mask < lower->mask) { 398 | YOMM11_TRACE(cout << "create new group" << endl); 399 | dim_groups.insert(lower, g); 400 | } else { 401 | YOMM11_TRACE(cout << "add " << pc << " to existing group " << lower->methods << endl); 402 | lower->classes.push_back(pc); 403 | } 404 | }); 405 | 406 | step *= dim_groups.size(); 407 | 408 | YOMM11_TRACE(cout << "assign slots" << endl); 409 | 410 | int offset = 0; 411 | 412 | for (auto& group : dim_groups) { 413 | for (auto pc : group.classes) { 414 | YOMM11_TRACE(cout << pc << ": " << offset << endl); 415 | pc->mmt[mm.slots[dim]].index = offset; 416 | } 417 | ++offset; 418 | } 419 | 420 | ++dim; 421 | } 422 | 423 | dispatch_table = mm.allocate_dispatch_table(step); 424 | } 425 | 426 | void grouping_resolver::make_table() { 427 | YOMM11_TRACE(cout << "Creating dispatch table for " << mm.name << endl); 428 | 429 | emit_at = 0; 430 | resolve(dims - 1, ~bitvec(mm.methods.size())); 431 | 432 | const int first_slot = mm.slots[0]; 433 | 434 | bitvec once; 435 | 436 | for (auto& group : groups[0]) { 437 | for (auto pc : group.classes) { 438 | if (once.size() <= pc->index) { 439 | once.resize(pc->index + 1); 440 | } 441 | 442 | if (!once[pc->index]) { 443 | once[pc->index] = true; 444 | pc->mmt[first_slot].ptr = dispatch_table + pc->mmt[first_slot].index; 445 | } 446 | } 447 | } 448 | } 449 | 450 | void grouping_resolver::resolve(int dim, const bitvec& candidates) { 451 | using namespace std; 452 | YOMM11_TRACE(cout << "resolve dim = " << dim << endl); 453 | 454 | for (auto& group : groups[dim]) { 455 | if (dim == 0) { 456 | specialization_base* best = find_best(candidates & group.mask); 457 | YOMM11_TRACE(cout << "install " << best << " at offset " << emit_at << endl); 458 | mm.emit(best, emit_at++); 459 | } else { 460 | resolve(dim - 1, candidates & group.mask); 461 | } 462 | } 463 | 464 | YOMM11_TRACE(cout << "exiting dim " << dim << endl); 465 | } 466 | 467 | specialization_base* grouping_resolver::find_best(const vector& candidates) { 468 | using namespace std; 469 | 470 | vector best; 471 | 472 | for (auto method : candidates) { 473 | auto best_iter = best.begin(); 474 | 475 | while (best_iter != best.end()) { 476 | if (method->specializes(*best_iter)) { 477 | YOMM11_TRACE(cout << method << " specializes " << *best_iter << ", removed\n"); 478 | best_iter = best.erase(best_iter); 479 | } else if ((*best_iter)->specializes(method)) { 480 | YOMM11_TRACE(cout << *best_iter << " specializes " << method << ", removed\n"); 481 | best_iter = best.end(); 482 | method = 0; 483 | } else { 484 | best_iter++; 485 | } 486 | } 487 | 488 | if (method) { 489 | YOMM11_TRACE(cout << method << " kept\n"); 490 | best.push_back(method); 491 | } 492 | } 493 | 494 | return best.size() == 0 ? &specialization_base::undefined 495 | : best.size() == 1 ? best.front() 496 | : &specialization_base::ambiguous; 497 | } 498 | 499 | specialization_base* grouping_resolver::find_best(const bitvec& mask) { 500 | vector candidates; 501 | copy_if(mm.methods.begin(), mm.methods.end(), back_inserter(candidates), 502 | [&](specialization_base* method) { return mask[method->index]; }); 503 | return find_best(candidates); 504 | } 505 | 506 | void grouping_resolver::find_applicable(int dim, const yomm11_class* pc, vector& methods) { 507 | copy_if( 508 | mm.methods.begin(), mm.methods.end(), 509 | back_inserter(methods), 510 | [=](specialization_base* pm) { return pc->conforms_to(*pm->args[dim]); }); 511 | } 512 | 513 | void grouping_resolver::assign_next() { 514 | for (specialization_base* pm : mm.methods) { 515 | vector candidates; 516 | copy_if( 517 | mm.methods.begin(), mm.methods.end(), back_inserter(candidates), 518 | [&](specialization_base* other) { 519 | return pm != other && pm->specializes(other); 520 | }); 521 | YOMM11_TRACE(cout << "calculating next for " << pm << ", candidates:\n"); 522 | YOMM11_TRACE(copy(candidates.begin(), candidates.end(), ostream_iterator(cout, "\n"))); 523 | auto best = find_best(candidates); 524 | YOMM11_TRACE(cout << "next is: " << best << endl); 525 | mm.emit_next(pm, best); 526 | } 527 | } 528 | 529 | void grouping_resolver::make_mask(const vector& methods, bitvec& mask) { 530 | mask.resize(mm.methods.size()); 531 | 532 | for (auto pm : methods) { 533 | mask[pm->index] = true; 534 | } 535 | } 536 | 537 | namespace detail { 538 | 539 | #ifdef YOMM11_ENABLE_TRACE 540 | 541 | std::ostream& operator <<(std::ostream& os, const yomm11_class* pc) { 542 | if (pc) { 543 | os << pc->name; 544 | } else { 545 | os << "(null)"; 546 | } 547 | return os; 548 | } 549 | 550 | ostream& operator <<(ostream& os, specialization_base* method) { 551 | using namespace std; 552 | 553 | if (method == &specialization_base::undefined) { 554 | return os << "undefined"; 555 | } 556 | 557 | if (method == &specialization_base::ambiguous) { 558 | return os << "ambiguous"; 559 | } 560 | 561 | const char* sep = "("; 562 | 563 | for (yomm11_class* pc : method->args) { 564 | os << sep; 565 | sep = ", "; 566 | os << pc->name; 567 | } 568 | 569 | return os << ")"; 570 | } 571 | 572 | ostream& operator <<(ostream& os, const method_base* mm) { 573 | return os << mm->name << mm->vargs; 574 | } 575 | 576 | ostream& operator <<(ostream& os, const vector& methods) { 577 | using namespace std; 578 | const char* sep = ""; 579 | 580 | for (specialization_base* pm : methods) { 581 | os << sep; 582 | sep = " "; 583 | os << pm; 584 | } 585 | 586 | return os; 587 | } 588 | 589 | ostream& operator <<(ostream& os, const method_base* mm) { 590 | return os << "mm" << mm->vargs; 591 | } 592 | 593 | #endif 594 | 595 | ostream& operator <<(ostream& os, const bitvec& v) { 596 | for (int i = 0; i < v.size(); i++) { 597 | os << (v[i] ? 1 : 0); 598 | } 599 | return os; 600 | } 601 | 602 | } // detail 603 | 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt 2 | # Copyright (c) 2013 Jean-Louis Leroy 3 | # Distributed under the Boost Software License, Version 1.0. (See 4 | # accompanying file LICENSE_1_0.txt or copy at 5 | # http:#www.boost.org/LICENSE_1_0.txt) 6 | 7 | add_executable(tests tests.cpp) 8 | target_link_libraries (tests yomm11) 9 | 10 | add_executable(order12 order1.cpp order2.cpp) 11 | target_link_libraries (order12 yomm11) 12 | 13 | add_executable(order21 order2.cpp order1.cpp) 14 | target_link_libraries (order21 yomm11) 15 | 16 | if(NOT MSVC) 17 | add_executable(benchmarks benchmarks.cpp benchmarks_fast.cpp) 18 | SET_SOURCE_FILES_PROPERTIES(benchmarks.cpp PROPERTIES COMPILE_FLAGS -O2) 19 | SET_SOURCE_FILES_PROPERTIES(benchmarks_fast.cpp PROPERTIES COMPILE_FLAGS -O2) 20 | target_link_libraries (benchmarks yomm11) 21 | endif() -------------------------------------------------------------------------------- /tests/adjust.hpp: -------------------------------------------------------------------------------- 1 | // adjust.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | struct X : selector { 8 | MM_CLASS(X); 9 | 10 | X() { 11 | MM_INIT(); 12 | } 13 | }; 14 | 15 | struct A : VIRTUAL X { 16 | MM_CLASS(A, X); 17 | int pad; 18 | int val; 19 | 20 | A() { 21 | MM_INIT(); 22 | pad = -1; 23 | } 24 | }; 25 | 26 | struct Pad { 27 | char x[200]; 28 | }; 29 | 30 | struct B : Pad, VIRTUAL X { 31 | MM_CLASS(B, X); 32 | int val; 33 | 34 | B() { 35 | MM_INIT(); 36 | } 37 | }; 38 | 39 | MULTI_METHOD(foo, int, const virtual_&, const virtual_&); 40 | 41 | BEGIN_SPECIALIZATION(foo, int, const A& x, const A& y) { 42 | return x.val + y.val; 43 | } END_SPECIALIZATION; 44 | 45 | BEGIN_SPECIALIZATION(foo, int, const A& x, const B& y) { 46 | return x.val - y.val; 47 | } END_SPECIALIZATION; 48 | 49 | BEGIN_SPECIALIZATION(foo, int, const B& x, const B& y) { 50 | return x.val * y.val; 51 | } END_SPECIALIZATION; 52 | -------------------------------------------------------------------------------- /tests/animals.hpp: -------------------------------------------------------------------------------- 1 | // animals.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | struct Animal : yorel::multi_methods::selector { 8 | MM_CLASS(Animal); 9 | Animal() { 10 | MM_INIT(); 11 | } 12 | }; 13 | 14 | struct Herbivore : Animal { 15 | MM_CLASS(Herbivore, Animal); 16 | Herbivore() { 17 | MM_INIT(); 18 | } 19 | }; 20 | 21 | struct Carnivore : Animal { 22 | MM_CLASS(Carnivore, Animal); 23 | Carnivore() { 24 | MM_INIT(); 25 | } 26 | }; 27 | 28 | struct Cow : Herbivore { 29 | MM_CLASS(Cow, Herbivore); 30 | Cow() { 31 | MM_INIT(); 32 | } 33 | }; 34 | 35 | struct Wolf : Carnivore { 36 | MM_CLASS(Wolf, Carnivore); 37 | Wolf() { 38 | MM_INIT(); 39 | } 40 | }; 41 | 42 | struct Tiger : Carnivore { 43 | MM_CLASS(Tiger, Carnivore); 44 | Tiger() { 45 | MM_INIT(); 46 | } 47 | }; 48 | 49 | struct Interface : selector { 50 | MM_CLASS(Interface); 51 | Interface() { 52 | MM_INIT(); 53 | } 54 | }; 55 | 56 | struct Terminal : Interface { 57 | MM_CLASS(Terminal, Interface); 58 | Terminal() { 59 | MM_INIT(); 60 | } 61 | }; 62 | 63 | struct Window : Interface { 64 | MM_CLASS(Window, Interface); 65 | Window() { 66 | MM_INIT(); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /tests/benchmarks.cpp: -------------------------------------------------------------------------------- 1 | // benchmarks.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // chrt -f 99 ./benchmarks 8 | // 10000000 iterations, time in millisecs 9 | // virtual function, do_nothing : 75.50 10 | // open method, intrusive, do_nothing : 100.56 11 | // open method, foreign, do_nothing : 1397.75 12 | // virtual function, do_something : 1541.11 13 | // open method, intrusive, do_something : 1608.20 14 | // open method, foreign, do_something : 2607.76 15 | // virtual function, 2-dispatch, do_nothing : 250.75 16 | // open method with 2 args, intrusive, do_nothing : 150.77 17 | // open method with 2 args, foreign, do_nothing : 2832.26 18 | 19 | // results obtained on my computer (ThinkPad x200s): 20 | // processor : 0 & 1 21 | // vendor_id : GenuineIntel 22 | // cpu family : 6 23 | // model : 23 24 | // model name : Intel(R) Core(TM)2 Duo CPU L9400 @ 1.86GHz 25 | // stepping : 10 26 | // microcode : 0xa0c 27 | // cpu MHz : 800.000 28 | // cache size : 6144 KB 29 | // cpu cores : 2 30 | // fpu : yes 31 | // fpu_exception: yes 32 | // cpuid level : 13 33 | // wp : yes 34 | // bogomips : 3724.05 35 | // clflush size : 64 36 | 37 | #include 38 | #include 39 | #include 40 | #include "benchmarks.hpp" 41 | 42 | using namespace std; 43 | using namespace std::chrono; 44 | using yorel::multi_methods::virtual_; 45 | 46 | namespace intrusive { 47 | 48 | MULTI_METHOD(do_nothing, void, virtual_&); 49 | 50 | BEGIN_SPECIALIZATION(do_nothing, void, object&) { 51 | } END_SPECIALIZATION; 52 | 53 | MULTI_METHOD(do_something, double, virtual_&, double x, double a, double b, double c); 54 | 55 | BEGIN_SPECIALIZATION(do_something, double, object&, double x, double a, double b, double c) { 56 | return log(a * x * x + b * x + c); 57 | } END_SPECIALIZATION; 58 | 59 | MULTI_METHOD(do_nothing_2, void, virtual_&, virtual_&); 60 | 61 | BEGIN_SPECIALIZATION(do_nothing_2, void, object&, object&) { 62 | } END_SPECIALIZATION; 63 | 64 | } 65 | 66 | namespace vbase { 67 | 68 | MULTI_METHOD(do_nothing, void, virtual_&); 69 | 70 | BEGIN_SPECIALIZATION(do_nothing, void, object&) { 71 | } END_SPECIALIZATION; 72 | 73 | MULTI_METHOD(do_something, double, virtual_&, double x, double a, double b, double c); 74 | 75 | BEGIN_SPECIALIZATION(do_something, double, derived&, double x, double a, double b, double c) { 76 | return log(a * x * x + b * x + c); 77 | } END_SPECIALIZATION; 78 | 79 | MULTI_METHOD(do_nothing_2, void, virtual_&, virtual_&); 80 | 81 | BEGIN_SPECIALIZATION(do_nothing_2, void, object&, object&) { 82 | } END_SPECIALIZATION; 83 | 84 | } 85 | 86 | namespace foreign { 87 | 88 | struct object { 89 | virtual ~object() { } 90 | }; 91 | 92 | MM_FOREIGN_CLASS(object); 93 | 94 | MULTI_METHOD(do_nothing, void, virtual_&); 95 | 96 | BEGIN_SPECIALIZATION(do_nothing, void, object&) { 97 | } END_SPECIALIZATION; 98 | 99 | MULTI_METHOD(do_nothing_2, void, virtual_&, virtual_&); 100 | 101 | BEGIN_SPECIALIZATION(do_nothing_2, void, object&, object&) { 102 | } END_SPECIALIZATION; 103 | 104 | MULTI_METHOD(do_something, double, virtual_&, double x, double a, double b, double c); 105 | 106 | BEGIN_SPECIALIZATION(do_something, double, object&, double x, double a, double b, double c) { 107 | return log(a * x * x + b * x + c); 108 | } END_SPECIALIZATION; 109 | } 110 | 111 | using time_type = decltype(high_resolution_clock::now()); 112 | 113 | void post(const string& description, time_type start, time_type end) { 114 | cout << setw(50) << left << description << ": " << setw(8) << fixed << right << setprecision(2) << duration(end - start).count() << endl; 115 | } 116 | 117 | struct benchmark { 118 | benchmark(const string& label) : label(label), start(high_resolution_clock::now()) { 119 | } 120 | 121 | ~benchmark() { 122 | auto end = high_resolution_clock::now(); 123 | cout << setw(50) << left << label << ": " 124 | << setw(8) << fixed << right << setprecision(2) 125 | << duration(end - start).count() << endl; 126 | } 127 | 128 | const string label; 129 | decltype(high_resolution_clock::now()) start; 130 | }; 131 | 132 | int main() { 133 | yorel::multi_methods::initialize(); 134 | 135 | const int repeats = 10 * 1000 * 1000; 136 | 137 | { 138 | auto pf = new foreign::object; 139 | auto pi = intrusive::object::make(); 140 | 141 | cout << repeats << " iterations, time in millisecs\n"; 142 | 143 | { 144 | benchmark b("virtual function, do_nothing"); 145 | for (int i = 0; i < repeats; i++) 146 | pi->do_nothing(); 147 | } 148 | 149 | { 150 | benchmark b("open method, intrusive, do_nothing"); 151 | for (int i = 0; i < repeats; i++) 152 | intrusive::do_nothing(*pi); 153 | } 154 | 155 | { 156 | benchmark b("open method, foreign, do_nothing"); 157 | for (int i = 0; i < repeats; i++) 158 | foreign::do_nothing(*pf); 159 | } 160 | 161 | { 162 | benchmark b("virtual function, do_something"); 163 | for (int i = 0; i < repeats; i++) 164 | pi->do_something(1, 2, 3, 4); 165 | } 166 | 167 | { 168 | benchmark b("open method, intrusive, do_something"); 169 | for (int i = 0; i < repeats; i++) 170 | intrusive::do_something(*pi, 1, 2, 3, 4); 171 | } 172 | 173 | { 174 | benchmark b("open method, foreign, do_something"); 175 | for (int i = 0; i < repeats; i++) 176 | foreign::do_something(*pf, 1, 2, 3, 4); 177 | } 178 | 179 | // double dispatch 180 | 181 | { 182 | benchmark b("virtual function, 2-dispatch, do_nothing"); 183 | for (int i = 0; i < repeats; i++) 184 | pi->dd1_do_nothing(pi); 185 | } 186 | 187 | { 188 | benchmark b("open method with 2 args, intrusive, do_nothing"); 189 | for (int i = 0; i < repeats; i++) 190 | intrusive::do_nothing_2(*pi, *pi); 191 | } 192 | 193 | { 194 | benchmark b("open method with 2 args, foreign, do_nothing"); 195 | for (int i = 0; i < repeats; i++) 196 | foreign::do_nothing_2(*pf, *pf); 197 | } 198 | } 199 | 200 | // virtual inheritance 201 | { 202 | auto pi = vbase::object::make(); 203 | 204 | { 205 | benchmark b("virtual function, vbase, do_nothing"); 206 | for (int i = 0; i < repeats; i++) 207 | pi->do_nothing(); 208 | } 209 | 210 | { 211 | benchmark b("open method, vbase, do_nothing"); 212 | for (int i = 0; i < repeats; i++) 213 | vbase::do_nothing(*pi); 214 | } 215 | 216 | { 217 | benchmark b("virtual function, vbase, do_something"); 218 | for (int i = 0; i < repeats; i++) 219 | pi->do_something(1, 2, 3, 4); 220 | } 221 | 222 | { 223 | benchmark b("open method, vbase, do_something"); 224 | for (int i = 0; i < repeats; i++) 225 | vbase::do_something(*pi, 1, 2, 3, 4); 226 | } 227 | 228 | // double dispatch 229 | 230 | { 231 | benchmark b("virtual function, 2-dispatch, vbase, do_nothing"); 232 | for (int i = 0; i < repeats; i++) 233 | pi->dd1_do_nothing(pi); 234 | } 235 | 236 | { 237 | benchmark b("open method with 2 args, vbase, do_nothing"); 238 | for (int i = 0; i < repeats; i++) 239 | vbase::do_nothing_2(*pi, *pi); 240 | } 241 | } 242 | 243 | return 0; 244 | } 245 | -------------------------------------------------------------------------------- /tests/benchmarks.hpp: -------------------------------------------------------------------------------- 1 | // benchmarks.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | namespace intrusive { 10 | 11 | struct object : yorel::multi_methods::selector { 12 | 13 | MM_CLASS(object); 14 | 15 | static object* make(); 16 | 17 | object() { 18 | MM_INIT(); 19 | } 20 | 21 | virtual void do_nothing(); 22 | virtual double do_something(double x, double a, double b, double c); 23 | virtual void dd1_do_nothing(object* pf); 24 | virtual void dd2_do_nothing(object* pf); 25 | }; 26 | 27 | } 28 | 29 | namespace vbase { 30 | 31 | struct object : yorel::multi_methods::selector { 32 | 33 | MM_CLASS(object); 34 | 35 | static object* make(); 36 | 37 | object() { 38 | MM_INIT(); 39 | } 40 | 41 | virtual void do_nothing() = 0; 42 | virtual double do_something(double x, double a, double b, double c) = 0; 43 | virtual void dd1_do_nothing(object* pf) = 0; 44 | virtual void dd2_do_nothing(object* pf) = 0; 45 | }; 46 | 47 | struct derived : virtual object { 48 | 49 | MM_CLASS(derived, object); 50 | 51 | derived() { 52 | MM_INIT(); 53 | } 54 | 55 | virtual void do_nothing(); 56 | virtual double do_something(double x, double a, double b, double c); 57 | virtual void dd1_do_nothing(object* pf); 58 | virtual void dd2_do_nothing(object* pf); 59 | }; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /tests/benchmarks_fast.cpp: -------------------------------------------------------------------------------- 1 | // benchmarks_fast.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "benchmarks.hpp" 8 | 9 | namespace intrusive { 10 | 11 | object* object::make() { 12 | return new object; 13 | } 14 | 15 | double object::do_something(double x, double a, double b, double c) { 16 | return log(a * x * x + b * x + c); 17 | } 18 | 19 | void object::do_nothing() { 20 | } 21 | 22 | void object::dd1_do_nothing(object* pf) { 23 | pf->dd2_do_nothing(pf); 24 | } 25 | 26 | void object::dd2_do_nothing(object* pf) { 27 | } 28 | 29 | } 30 | 31 | namespace vbase { 32 | 33 | object* object::make() { 34 | return new derived; 35 | } 36 | 37 | double derived::do_something(double x, double a, double b, double c) { 38 | return log(a * x * x + b * x + c); 39 | } 40 | 41 | void derived::do_nothing() { 42 | } 43 | 44 | void derived::dd1_do_nothing(object* pf) { 45 | pf->dd2_do_nothing(pf); 46 | } 47 | 48 | void derived::dd2_do_nothing(object* pf) { 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /tests/mi.hpp: -------------------------------------------------------------------------------- 1 | // mi.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | struct Animal : yorel::multi_methods::selector { 8 | MM_CLASS(Animal); 9 | Animal() { 10 | MM_INIT(); 11 | } 12 | }; 13 | 14 | struct Herbivore : virtual Animal { 15 | MM_CLASS(Herbivore, Animal); 16 | Herbivore() { 17 | MM_INIT(); 18 | } 19 | }; 20 | 21 | struct Predator : virtual Animal { 22 | MM_CLASS(Predator, Animal); 23 | Predator() { 24 | MM_INIT(); 25 | } 26 | }; 27 | 28 | struct Male : virtual Animal { 29 | MM_CLASS(Male, Animal); 30 | Male() { 31 | MM_INIT(); 32 | } 33 | }; 34 | 35 | struct Female : virtual Animal { 36 | MM_CLASS(Female, Animal); 37 | Female() { 38 | MM_INIT(); 39 | } 40 | }; 41 | 42 | struct Horse : Herbivore { 43 | MM_CLASS(Horse, Herbivore); 44 | Horse() { 45 | MM_INIT(); 46 | } 47 | }; 48 | 49 | struct Stallion : Male, Horse { 50 | MM_CLASS(Stallion, Male, Horse); 51 | Stallion() { 52 | MM_INIT(); 53 | } 54 | }; 55 | 56 | struct Mare : Female, Horse { 57 | MM_CLASS(Mare, Female, Horse); 58 | Mare() { 59 | MM_INIT(); 60 | } 61 | }; 62 | 63 | struct Wolf : Predator { 64 | MM_CLASS(Wolf, Predator); 65 | Wolf() { 66 | MM_INIT(); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /tests/order.hpp: -------------------------------------------------------------------------------- 1 | // order.hpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | struct role : yorel::multi_methods::selector { 11 | MM_CLASS(role); 12 | role() { 13 | MM_INIT(); 14 | } 15 | }; 16 | 17 | struct expense : yorel::multi_methods::selector { 18 | MM_CLASS(expense); 19 | expense() { 20 | MM_INIT(); 21 | } 22 | }; 23 | 24 | struct plane : expense { 25 | MM_CLASS(plane, expense); 26 | plane() { 27 | MM_INIT(); 28 | } 29 | }; 30 | 31 | struct reason : yorel::multi_methods::selector { 32 | MM_CLASS(reason); 33 | reason() { 34 | MM_INIT(); 35 | } 36 | }; 37 | 38 | struct comfort : reason { 39 | MM_CLASS(comfort, reason); 40 | comfort() { 41 | MM_INIT(); 42 | } 43 | }; 44 | 45 | MULTI_METHOD(approve, bool, const yorel::multi_methods::virtual_&, const yorel::multi_methods::virtual_&, const yorel::multi_methods::virtual_&); 46 | 47 | extern std::unique_ptr make_ceo(); 48 | -------------------------------------------------------------------------------- /tests/order1.cpp: -------------------------------------------------------------------------------- 1 | // order2.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "order.hpp" 8 | #include 9 | 10 | using namespace std; 11 | 12 | using yorel::multi_methods::virtual_; 13 | 14 | struct business : reason { 15 | MM_CLASS(business, reason); 16 | business() { 17 | MM_INIT(); 18 | } 19 | }; 20 | 21 | struct manager : role { 22 | MM_CLASS(manager, role); 23 | manager() { 24 | MM_INIT(); 25 | } 26 | }; 27 | 28 | struct cab : expense { 29 | MM_CLASS(cab, expense); 30 | cab() { 31 | MM_INIT(); 32 | } 33 | }; 34 | 35 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const role&, const reason&) { 36 | return false; 37 | } END_SPECIALIZATION; 38 | 39 | BEGIN_SPECIALIZATION(approve, bool, const cab&, const manager&, const business&) { 40 | return true; 41 | } END_SPECIALIZATION; 42 | 43 | #define demo(exp) cout << #exp << " -> " << exp << endl 44 | 45 | int main() { 46 | cout << boolalpha; 47 | yorel::multi_methods::initialize(); 48 | unique_ptr ceo = make_ceo(); 49 | // ceo does as he pleases 50 | demo( approve(plane(), *ceo, comfort()) ); 51 | demo( approve(cab(), *ceo, business()) ); 52 | // managers only take cabs for business 53 | demo( approve(cab(), manager(), business()) ); 54 | demo( approve(cab(), manager(), comfort()) ); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /tests/order2.cpp: -------------------------------------------------------------------------------- 1 | // order2.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "order.hpp" 8 | #include 9 | 10 | using namespace std; 11 | 12 | using yorel::multi_methods::virtual_; 13 | 14 | struct ceo : role { 15 | MM_CLASS(ceo, role); 16 | ceo() { 17 | MM_INIT(); 18 | } 19 | }; 20 | 21 | BEGIN_SPECIALIZATION(approve, bool, const expense&, const ceo&, const reason&) { 22 | return true; 23 | } END_SPECIALIZATION; 24 | 25 | unique_ptr make_ceo() { 26 | return unique_ptr(new ceo()); 27 | } 28 | -------------------------------------------------------------------------------- /tests/run: -------------------------------------------------------------------------------- 1 | cmake CMakeLists.txt -DCMAKE_CXX_COMPILER=g++ && make && make test && cmake CMakeLists.txt -DCMAKE_CXX_COMPILER=clang++ && make && make test 2 | -------------------------------------------------------------------------------- /tests/tests.cpp: -------------------------------------------------------------------------------- 1 | // tests.cpp 2 | // Copyright (c) 2013 Jean-Louis Leroy 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | //#define YOREL_MM_ENABLE_TRACE 8 | 9 | #ifdef YOREL_MM_ENABLE_TRACE 10 | #include "../src/yomm11.cpp" 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "util/join.hpp" 21 | 22 | using namespace std; 23 | using namespace yorel::methods; 24 | using namespace yorel::methods::detail; 25 | 26 | #define test(exp, res) _test(__FILE__, __LINE__, #exp, exp, #res, res) 27 | #define testx(exp, res) _test(__FILE__, __LINE__, #exp, exp, 0, res) 28 | 29 | namespace { 30 | int success, failure; 31 | } 32 | 33 | using methods = vector; 34 | 35 | #ifndef YOMM11_ENABLE_TRACE 36 | 37 | ostream& operator <<(ostream& os, const vector& methods) { 38 | using namespace std; 39 | const char* sep = ""; 40 | 41 | for (specialization_base* pm : methods) { 42 | os << sep; 43 | sep = " "; 44 | os << pm; 45 | } 46 | 47 | return os; 48 | } 49 | 50 | #endif 51 | 52 | template 53 | bool _test(const char* file, int line, const char* test, const T1& got, const char* expected_expr, const T2& expected) { 54 | string ee; 55 | if (expected_expr) { 56 | ee = expected_expr; 57 | } else { 58 | ostringstream os; 59 | os << expected; 60 | ee = os.str(); 61 | } 62 | bool ok = got == expected; 63 | if (ok) { 64 | cout << setw(3) << test << " returns " << ee << ", ok.\n"; 65 | ++success; 66 | } else { 67 | cout << file << ":" << line << ": error: " << test << ": " << got << ", expected " << ee << ".\n"; 68 | ++failure; 69 | } 70 | return ok; 71 | } 72 | 73 | #define PP_CAT(X, Y) PP_CAT1(X, Y) 74 | #define PP_CAT1(X, Y) X ## Y 75 | 76 | #define DO void PP_CAT(fun, __LINE__)(); int PP_CAT(var, __LINE__) = (PP_CAT(fun, __LINE__)(), 1); void PP_CAT(fun, __LINE__)() 77 | 78 | #define show(e) #e << " = " << (e) 79 | 80 | template 81 | bool throws(function fun) { 82 | try { 83 | fun(); 84 | } catch (Ex) { 85 | return true; 86 | } catch (...) { 87 | } 88 | 89 | return false; 90 | } 91 | 92 | unsigned long binary(const char* digits) { 93 | unsigned long bits = 0; 94 | 95 | while (*digits) { 96 | bits <<= 1; 97 | bits |= *digits++ == '1'; 98 | } 99 | 100 | return bits; 101 | } 102 | 103 | DO { 104 | cout << boolalpha; 105 | } 106 | 107 | namespace single_inheritance { 108 | 109 | #include "animals.hpp" 110 | } 111 | 112 | namespace slot_allocation_tests { 113 | 114 | struct X : selector { 115 | MM_CLASS(X); 116 | X() { MM_INIT(); } 117 | }; 118 | 119 | MULTI_METHOD(m_x, int, const virtual_&); 120 | 121 | struct A : X { 122 | MM_CLASS(A, X); 123 | A() { MM_INIT(); } 124 | }; 125 | 126 | MULTI_METHOD(m_a, int, const virtual_&); 127 | 128 | struct B : virtual A { 129 | MM_CLASS(B, A); 130 | B() { MM_INIT(); } 131 | }; 132 | 133 | MULTI_METHOD(m_b, int, const virtual_&); 134 | 135 | struct C : virtual A { 136 | MM_CLASS(C, A); 137 | C() { MM_INIT(); } 138 | }; 139 | 140 | MULTI_METHOD(m_c, int, const virtual_&); 141 | 142 | struct D : virtual A { 143 | MM_CLASS(D, A); 144 | D() { MM_INIT(); } 145 | }; 146 | 147 | MULTI_METHOD(m_d, int, const virtual_&); 148 | 149 | struct BC : B, C { 150 | MM_CLASS(BC, B, C); 151 | BC() { MM_INIT(); } 152 | }; 153 | 154 | MULTI_METHOD(m_bc, int, const virtual_&); 155 | 156 | struct CD : C, D { 157 | MM_CLASS(CD, C, D); 158 | CD() { MM_INIT(); } 159 | }; 160 | 161 | MULTI_METHOD(m_cd, int, const virtual_&); 162 | 163 | struct Y : virtual X { 164 | MM_CLASS(Y, X); 165 | Y() { MM_INIT(); } 166 | }; 167 | 168 | MULTI_METHOD(m_y, int, const virtual_&); 169 | } 170 | 171 | namespace grouping_resolver_tests { 172 | 173 | #include "animals.hpp" 174 | 175 | #define MAKE_CLASS(Class, Base) \ 176 | struct Class : Base { \ 177 | MM_CLASS(Class, Base); \ 178 | Class() { \ 179 | MM_INIT(); \ 180 | } \ 181 | } 182 | 183 | MAKE_CLASS(Mobile, Interface); 184 | MAKE_CLASS(MSWindows, Window); 185 | MAKE_CLASS(X, Window); 186 | MAKE_CLASS(Nokia, Mobile); 187 | MAKE_CLASS(Samsung, Mobile); 188 | 189 | enum action { whatever, print_herbivore, draw_herbivore, print_carnivore, draw_carnivore, mobile }; 190 | 191 | MULTI_METHOD(display, action, const virtual_&, const virtual_&); 192 | 193 | // 0 194 | BEGIN_SPECIALIZATION(display, action, const Herbivore& a, const Terminal& b) { 195 | return print_herbivore; 196 | } END_SPECIALIZATION; 197 | 198 | // 1 199 | BEGIN_SPECIALIZATION(display, action, const Herbivore& a, const Window& b) { 200 | return draw_herbivore; 201 | } END_SPECIALIZATION; 202 | 203 | // 2 204 | BEGIN_SPECIALIZATION(display, action, const Carnivore& a, const Terminal& b) { 205 | return print_carnivore; 206 | } END_SPECIALIZATION; 207 | 208 | // 3 209 | BEGIN_SPECIALIZATION(display, action, const Carnivore& a, const Window& b) { 210 | return draw_carnivore; 211 | } END_SPECIALIZATION; 212 | 213 | // 4 214 | BEGIN_SPECIALIZATION(display, action, const Animal& a, const Mobile& b) { 215 | return mobile; 216 | } END_SPECIALIZATION; 217 | 218 | } 219 | 220 | #if !defined(__clang__) && !defined(_MSC_VER) 221 | 222 | namespace init_tests { 223 | 224 | #include "animals.hpp" 225 | 226 | MULTI_METHOD(encounter, string, const virtual_&, const virtual_&); 227 | 228 | BEGIN_SPECIALIZATION(encounter, string, const Animal&, const Animal&) { 229 | return "ignore"; 230 | } END_SPECIALIZATION; 231 | 232 | DO { 233 | yorel::methods::initialize(); 234 | test(encounter(Cow(), Wolf()), "ignore"); 235 | test(encounter(Wolf(), Cow()), "ignore"); 236 | } 237 | 238 | BEGIN_SPECIALIZATION(encounter, string, const Herbivore&, const Carnivore&) { 239 | return "run"; 240 | } END_SPECIALIZATION; 241 | 242 | DO { 243 | yorel::methods::initialize(); 244 | test(encounter(Cow(), Wolf()), "run"); 245 | test(encounter(Wolf(), Cow()), "ignore"); 246 | } 247 | 248 | BEGIN_SPECIALIZATION(encounter, string, const Carnivore&, const Herbivore&) { 249 | return "hunt"; 250 | } END_SPECIALIZATION; 251 | 252 | DO { 253 | yorel::methods::initialize(); 254 | test(encounter(Cow(), Wolf()), "run"); 255 | test(encounter(Wolf(), Cow()), "hunt"); 256 | } 257 | 258 | struct Horse : Herbivore { 259 | MM_CLASS(Horse, Herbivore); 260 | Horse() { 261 | MM_INIT(); 262 | } 263 | }; 264 | 265 | DO { 266 | yorel::methods::initialize(); 267 | test(encounter(Horse(), Wolf()), "run"); 268 | test(encounter(Wolf(), Horse()), "hunt"); 269 | } 270 | } 271 | 272 | #endif 273 | 274 | namespace single_inheritance { 275 | 276 | MULTI_METHOD(encounter, string, virtual_&, virtual_&); 277 | 278 | BEGIN_SPECIALIZATION(encounter, string, Animal&, Animal&) { 279 | return "ignore"; 280 | } END_SPECIALIZATION; 281 | 282 | BEGIN_SPECIALIZATION(encounter, string, Carnivore&, Animal&) { 283 | return "hunt"; 284 | } END_SPECIALIZATION; 285 | 286 | BEGIN_SPECIALIZATION(encounter, string, Carnivore&, Carnivore&) { 287 | return "fight"; 288 | } END_SPECIALIZATION; 289 | 290 | BEGIN_SPECIALIZATION(encounter, string, Wolf&, Wolf&) { 291 | return "wag tail"; 292 | } END_SPECIALIZATION; 293 | 294 | BEGIN_SPECIALIZATION(encounter, string, Herbivore&, Carnivore&) { 295 | return "run"; 296 | } END_SPECIALIZATION; 297 | 298 | enum action { display_error, print_cow, draw_cow, print_wolf, draw_wolf, print_tiger, draw_tiger, print_herbivore, display_cow, print_animal }; 299 | 300 | MULTI_METHOD(display, action, virtual_&, virtual_&); 301 | 302 | BEGIN_SPECIALIZATION(display, action, Cow& a, Terminal& b) { 303 | return print_cow; 304 | } END_SPECIALIZATION; 305 | 306 | BEGIN_SPECIALIZATION(display, action, Wolf& a, Terminal& b) { 307 | return print_wolf; 308 | } END_SPECIALIZATION; 309 | 310 | BEGIN_SPECIALIZATION(display, action, Tiger& a, Terminal& b) { 311 | return print_tiger; 312 | } END_SPECIALIZATION; 313 | 314 | BEGIN_SPECIALIZATION(display, action, Cow& a, Window& b) { 315 | return draw_cow; 316 | } END_SPECIALIZATION; 317 | 318 | BEGIN_SPECIALIZATION(display, action, Wolf& a, Window& b) { 319 | return draw_wolf; 320 | } END_SPECIALIZATION; 321 | 322 | BEGIN_SPECIALIZATION(display, action, Tiger& a, Window& b) { 323 | return draw_tiger; 324 | } END_SPECIALIZATION; 325 | 326 | // following two are ambiguous, e.g. for (Cow, Terminal) 327 | 328 | BEGIN_SPECIALIZATION(display, action, Herbivore& a, Interface& b) { 329 | return display_error; 330 | } END_SPECIALIZATION; 331 | 332 | BEGIN_SPECIALIZATION(display, action, Animal& a, Terminal& b) { 333 | return display_error; 334 | } END_SPECIALIZATION; 335 | 336 | // following un-registered stuff is for unloading tests 337 | 338 | struct Donkey : Herbivore { }; 339 | 340 | template<> 341 | struct encounter_specialization : remove_const::type::specialization> { 342 | using body_signature = ::yorel::methods::detail::signature; 343 | static string body(Cow&, Cow&) { 344 | return "moo!"; 345 | } 346 | }; 347 | } 348 | 349 | namespace mi { 350 | #include "mi.hpp" 351 | 352 | MULTI_METHOD(encounter, string, virtual_&, virtual_&); 353 | 354 | BEGIN_SPECIALIZATION(encounter, string, Animal&, Animal&) { 355 | return "ignore"; 356 | } END_SPECIALIZATION; 357 | 358 | BEGIN_SPECIALIZATION(encounter, string, Stallion&, Mare&) { 359 | return "court"; 360 | } END_SPECIALIZATION; 361 | 362 | BEGIN_SPECIALIZATION(encounter, string, Predator&, Herbivore&) { 363 | return "hunt"; 364 | } END_SPECIALIZATION; 365 | } 366 | 367 | namespace adjust { 368 | #define VIRTUAL 369 | #include "adjust.hpp" 370 | #undef VIRTUAL 371 | } 372 | 373 | namespace adjust_virtual { 374 | #define VIRTUAL virtual 375 | #include "adjust.hpp" 376 | #undef VIRTUAL 377 | } 378 | 379 | namespace multi_roots { 380 | 381 | struct X : selector { 382 | MM_CLASS(X); 383 | 384 | int x; 385 | 386 | X() { 387 | MM_INIT(); 388 | } 389 | }; 390 | 391 | struct Y : selector { 392 | MM_CLASS(Y); 393 | 394 | int y; 395 | 396 | Y() { 397 | MM_INIT(); 398 | } 399 | }; 400 | 401 | struct XY : X, Y { 402 | MM_CLASS_MULTI(XY, X, Y); 403 | 404 | XY() { 405 | MM_INIT(); 406 | } 407 | }; 408 | 409 | MULTI_METHOD(mx, int, const virtual_&); 410 | 411 | BEGIN_SPECIALIZATION(mx, int, const X& x) { 412 | return x.x; 413 | } END_SPECIALIZATION; 414 | 415 | MULTI_METHOD(my, int, const virtual_&); 416 | 417 | BEGIN_SPECIALIZATION(my, int, const Y& y) { 418 | return y.y; 419 | } END_SPECIALIZATION; 420 | 421 | MULTI_METHOD(mxy, int, const virtual_&); 422 | 423 | BEGIN_SPECIALIZATION(mxy, int, const XY& xy) { 424 | return xy.x + xy.y; 425 | } END_SPECIALIZATION; 426 | 427 | } 428 | 429 | namespace multi_roots_foreign { 430 | 431 | struct X { 432 | virtual ~X() { } 433 | int x; 434 | }; 435 | 436 | MM_FOREIGN_CLASS(X); 437 | 438 | struct Y { 439 | virtual ~Y() { } 440 | int y; 441 | }; 442 | 443 | MM_FOREIGN_CLASS(Y); 444 | 445 | struct XY : X, Y { 446 | }; 447 | 448 | MM_FOREIGN_CLASS(XY, X, Y); 449 | 450 | MULTI_METHOD(mx, int, const virtual_&); 451 | 452 | BEGIN_SPECIALIZATION(mx, int, const X& x) { 453 | return x.x; 454 | } END_SPECIALIZATION; 455 | 456 | MULTI_METHOD(my, int, const virtual_&); 457 | 458 | BEGIN_SPECIALIZATION(my, int, const Y& y) { 459 | return y.y; 460 | } END_SPECIALIZATION; 461 | 462 | MULTI_METHOD(mxy, int, const virtual_&); 463 | 464 | BEGIN_SPECIALIZATION(mxy, int, const XY& xy) { 465 | return xy.x + xy.y; 466 | } END_SPECIALIZATION; 467 | 468 | } 469 | 470 | namespace repeated { 471 | 472 | struct X : selector { 473 | MM_CLASS(X); 474 | 475 | int x; 476 | 477 | X() { 478 | MM_INIT(); 479 | } 480 | }; 481 | 482 | struct A : X { 483 | MM_CLASS(A, X); 484 | 485 | int a; 486 | 487 | A() { 488 | MM_INIT(); 489 | } 490 | }; 491 | 492 | struct B : X { 493 | MM_CLASS(B, X); 494 | 495 | int b; 496 | 497 | B() { 498 | MM_INIT(); 499 | } 500 | }; 501 | 502 | struct AB : A, B { 503 | MM_CLASS_MULTI(AB, A, B); 504 | 505 | AB() { 506 | MM_INIT(); 507 | } 508 | }; 509 | } 510 | 511 | namespace yorel { 512 | namespace methods { 513 | template<> 514 | struct cast : 515 | cast_using_dynamic_cast { }; 516 | } 517 | } 518 | 519 | namespace repeated { 520 | 521 | MULTI_METHOD(mx, int, const virtual_&); 522 | 523 | BEGIN_SPECIALIZATION(mx, int, const X& x) { 524 | return x.x; 525 | } END_SPECIALIZATION; 526 | 527 | BEGIN_SPECIALIZATION(mx, int, const A& a) { 528 | return a.x + a.a; 529 | } END_SPECIALIZATION; 530 | 531 | BEGIN_SPECIALIZATION(mx, int, const AB& ab) { 532 | return ab.A::x + ab.B::x + ab.a + ab.b; 533 | } END_SPECIALIZATION; 534 | 535 | MULTI_METHOD(ma, int, const virtual_&); 536 | 537 | BEGIN_SPECIALIZATION(ma, int, const A& a) { 538 | return a.x + a.a; 539 | } END_SPECIALIZATION; 540 | 541 | MULTI_METHOD(mb, int, const virtual_&); 542 | 543 | BEGIN_SPECIALIZATION(mb, int, const B& b) { 544 | return b.x + b.b; 545 | } END_SPECIALIZATION; 546 | 547 | MULTI_METHOD(mab, int, const virtual_&); 548 | 549 | BEGIN_SPECIALIZATION(mab, int, const AB& ab) { 550 | return ab.A::x + ab.B::x + ab.a + ab.b; 551 | } END_SPECIALIZATION; 552 | 553 | } 554 | 555 | int main() { 556 | { 557 | using namespace single_inheritance; 558 | static_assert( 559 | is_same< 560 | typename extract_virtuals&, const virtual_&>::type, 561 | virtuals 562 | >::value, "not ok !!!"); 563 | 564 | static_assert( 565 | is_same< 566 | typename extract_virtuals&, int, virtual_&>::type, 567 | virtuals 568 | >::value, "not ok !!!"); 569 | 570 | static_assert( 571 | is_same< 572 | extract_method_virtuals< 573 | void(int, virtual_&, char, const virtual_&), 574 | void(int, Cow&, char, const Wolf&) 575 | >::type, 576 | virtuals >::value, "extraction of virtual method arguments"); 577 | 578 | cout << "\nClass registration" << endl; 579 | test(yomm11_class::of::the().bases.size(), 1); 580 | test(yomm11_class::of::the().bases[0] == &yomm11_class::of::the(), true); 581 | test(yomm11_class::of::the().specs.size(), 2); 582 | test(yomm11_class::of::the().specs[0] == &yomm11_class::of::the(), true); 583 | test(yomm11_class::of::the().specs[1] == &yomm11_class::of::the(), true); 584 | 585 | test(yomm11_class::of::the().root, yomm11_class::of::the().root); 586 | test(yomm11_class::of::the().root, yomm11_class::of::the().root); 587 | test(yomm11_class::of::the().root, yomm11_class::of::the().root); 588 | test(yomm11_class::of::the().root, yomm11_class::of::the().root); 589 | test(yomm11_class::of::the().root, yomm11_class::of::the().root); 590 | } 591 | 592 | { 593 | cout << "\n--- bitvec." << endl; 594 | 595 | { 596 | bitvec v; 597 | test(v.size(), 0); 598 | test(v.none(), true); 599 | } 600 | 601 | for (int n : { numeric_limits::digits - 1, 602 | numeric_limits::digits, 603 | numeric_limits::digits + 1 }) { 604 | cout << "n = " << n << endl; 605 | 606 | { 607 | bitvec v(n); 608 | test(v.size(), n); 609 | test(v.none(), true); 610 | v[0] = 1; 611 | test(v[0], true); 612 | v[0] = 0; 613 | test(v[0], false); 614 | v[n - 1] = 1; 615 | test(v[n - 1], true); 616 | v[n - 1] = 0; 617 | test(v[n - 1], false); 618 | } 619 | 620 | { 621 | bitvec v(n, binary("101")); 622 | test(v[0], true); 623 | test(v[1], false); 624 | test(v[2], true); 625 | } 626 | 627 | { 628 | bitvec v(n); 629 | v[0] = 1; 630 | v[n - 1] = 1; 631 | v.resize(n - 1); 632 | test(v[0], true); 633 | test(v[1], false); 634 | v.resize(n); 635 | test(v[n - 1], false); 636 | v[0] = false; 637 | test(v.none(), true); 638 | } 639 | 640 | { 641 | bitvec v(n); 642 | v[0] = 1; 643 | v[n - 1] = 1; 644 | v.resize(n + 1); 645 | test(v[0], true); 646 | test(v[1], false); 647 | test(v[n - 1], true); 648 | test(v[n], false); 649 | } 650 | 651 | { 652 | bitvec v(n); 653 | bitvec v2(v); 654 | v[0] = 1; 655 | v[n - 1] = 1; 656 | test(v2.none(), true); 657 | } 658 | 659 | { 660 | bitvec v(n); 661 | v[0] = 1; 662 | v[n - 1] = 1; 663 | bitvec v2; 664 | v2 = v; 665 | test(v2[0], true); 666 | test(v2[n - 1], true); 667 | v[0] = 0; 668 | v[n - 1] = 0; 669 | test(v2[0], true); 670 | test(v2[n - 1], true); 671 | } 672 | 673 | { 674 | bitvec v(n); 675 | test(v.none(), true); 676 | test((~~v).none(), true); 677 | v[0] = 1; 678 | v[n - 1] = 1; 679 | test(v.none(), false); 680 | v |= bitvec(n, binary("10")); 681 | test(v[0], true); 682 | test(v[1], true); 683 | test(v[n - 1], true); 684 | } 685 | 686 | { 687 | bitvec v1(n), v2(n); 688 | v1[0] = 1; 689 | v1[n - 1] = 1; 690 | v2[0] = 1; 691 | v2[n - 1] = 1; 692 | test(v1 == v2, true); 693 | } 694 | 695 | { 696 | bitvec v1(n), v2(n), v3(n); 697 | v1[0] = 1; 698 | v1[n - 1] = 1; 699 | v2[0] = 1; 700 | v2[n - 1] = 1; 701 | v1[1] = 1; 702 | v2[2] = 1; 703 | v3[0] = 1; 704 | v3[n - 1] = 1; 705 | test((v1 & v2) == v3, true); 706 | } 707 | 708 | { 709 | bitvec v(n); 710 | v[0] = 1; 711 | v[n - 1] = 1; 712 | test((v & bitvec(n)) == bitvec(n), true); 713 | } 714 | 715 | { 716 | bitvec v1(n), v2(n), v3(n); 717 | v1[0] = 1; 718 | v1[n - 1] = 1; 719 | v2[1] = 1; 720 | v2[n - 2] = 1; 721 | v3[0] = 1; 722 | v3[n - 1] = 1; 723 | v3[1] = 1; 724 | v3[n - 2] = 1; 725 | v1 |= v2; 726 | test(v1 == v3, true); 727 | } 728 | 729 | { 730 | bitvec v(n); 731 | v[0] = 1; 732 | v[n - 1] = 1; 733 | bitvec v2(~v); 734 | test(v2[0], false); 735 | test(v2[1], true); 736 | test(v2[n - 2], true); 737 | test(v2[n - 1], false); 738 | } 739 | } 740 | { 741 | bitvec v(2); 742 | test(v.none(), true); 743 | test(v[0], false); 744 | v[0] = 1; 745 | test(v.none(), false); 746 | test(v[0], true); 747 | test(v[1], false); 748 | v = ~v; 749 | test(v[0], false); 750 | test(v[1], true); 751 | } 752 | 753 | { 754 | bitvec v(3, binary("111")); 755 | test(v.size(), 3); 756 | test(v[0], true); 757 | test(v[1], true); 758 | test(v[2], true); 759 | v.resize(2); 760 | test(v.size(), 2); 761 | test(v[0], true); 762 | test(v[1], true); 763 | v.resize(3); 764 | test(v[0], true); 765 | test(v[1], true); 766 | test(v[2], false); 767 | } 768 | } 769 | 770 | { 771 | cout << "\n--- Slot allocation." << endl; 772 | 773 | using namespace slot_allocation_tests; 774 | 775 | // Init method implementations; this is normally done when the 776 | // first method is added. 777 | m_x.the(); 778 | m_a.the(); 779 | m_b.the(); 780 | m_c.the(); 781 | m_bc.the(); 782 | m_d.the(); 783 | m_cd.the(); 784 | m_y.the(); 785 | 786 | hierarchy_initializer init(yomm11_class::of::the()); 787 | 788 | init.collect_classes(); 789 | test( init.nodes.size(), 8); 790 | test( init.nodes.size(), 8); 791 | test( init.nodes[0], &yomm11_class::of::the() ); 792 | test( init.nodes[1], &yomm11_class::of::the() ); 793 | test( init.nodes[2], &yomm11_class::of::the() ); 794 | test( init.nodes[3], &yomm11_class::of::the() ); 795 | test( init.nodes[4], &yomm11_class::of::the() ); 796 | test( init.nodes[5], &yomm11_class::of::the() ); 797 | test( init.nodes[6], &yomm11_class::of::the() ); 798 | test( init.nodes[7], &yomm11_class::of::the() ); 799 | 800 | init.make_masks(); 801 | testx( init.nodes[0]->mask, bitvec(8, binary("11111111")) ); // X 802 | testx( init.nodes[1]->mask, bitvec(8, binary("01111110")) ); // A 803 | testx( init.nodes[2]->mask, bitvec(8, binary("00010100")) ); // B 804 | testx( init.nodes[3]->mask, bitvec(8, binary("01011000")) ); // C 805 | testx( init.nodes[4]->mask, bitvec(8, binary("00010000")) ); // BC 806 | testx( init.nodes[5]->mask, bitvec(8, binary("01100000")) ); // D 807 | testx( init.nodes[6]->mask, bitvec(8, binary("01000000")) ); // CD 808 | testx( init.nodes[7]->mask, bitvec(8, binary("10000000")) ); // Y 809 | 810 | init.assign_slots(); 811 | test(m_x.the().slots[0], 0); 812 | test(m_a.the().slots[0], 1); 813 | test(m_b.the().slots[0], 2); 814 | test(m_c.the().slots[0], 3); 815 | test(m_bc.the().slots[0], 4); 816 | test(m_d.the().slots[0], 2); 817 | test(m_cd.the().slots[0], 4); 818 | test(m_y.the().slots[0], 1); 819 | 820 | test(yomm11_class::of::the().mmt.size(), 1); 821 | test(yomm11_class::of::the().mmt.size(), 2); 822 | test(yomm11_class::of::the().mmt.size(), 3); 823 | test(yomm11_class::of::the().mmt.size(), 4); 824 | test(yomm11_class::of::the().mmt.size(), 5); 825 | test(yomm11_class::of::the().mmt.size(), 3); 826 | test(yomm11_class::of::the().mmt.size(), 5); 827 | test(yomm11_class::of::the().mmt.size(), 2); 828 | } 829 | 830 | { 831 | cout << "\n--- Slot allocation - multiple roots." << endl; 832 | using namespace multi_roots; 833 | 834 | test(yomm11_class::of::the().root, &yomm11_class::of::the()); 835 | test(yomm11_class::of::the().root, &yomm11_class::of::the()); 836 | 837 | { 838 | hierarchy_initializer init(yomm11_class::of::the()); 839 | init.collect_classes(); 840 | test(init.nodes.size(), 3); 841 | test(init.nodes[0], &init.root); 842 | test(init.nodes[0], &yomm11_class::of::the()); 843 | test(init.nodes[1], &yomm11_class::of::the()); 844 | test(init.nodes[2], &yomm11_class::of::the()); 845 | } 846 | 847 | { 848 | hierarchy_initializer init(yomm11_class::of::the()); 849 | init.collect_classes(); 850 | test(init.nodes.size(), 3); 851 | test(init.nodes[0], &yomm11_class::of::the()); 852 | test(init.nodes[1], &yomm11_class::of::the()); 853 | test(init.nodes[2], &yomm11_class::of::the()); 854 | } 855 | 856 | test(yomm11_class::of::the().is_root(), true); 857 | test(yomm11_class::of::the().is_root(), true); 858 | test(yomm11_class::of::the().is_root(), false); 859 | 860 | yomm11_class::add_to_initialize(&yomm11_class::of::the()); 861 | yomm11_class::add_to_initialize(&yomm11_class::of::the()); 862 | hierarchy_initializer::initialize(yomm11_class::of::the()); 863 | test(yomm11_class::to_initialize->empty() || 864 | find(yomm11_class::to_initialize->begin(), 865 | yomm11_class::to_initialize->end(), 866 | &yomm11_class::of::the()) == yomm11_class::to_initialize->end(), 867 | true); 868 | test(yomm11_class::to_initialize->empty() || 869 | find(yomm11_class::to_initialize->begin(), 870 | yomm11_class::to_initialize->end(), 871 | &yomm11_class::of::the()) == yomm11_class::to_initialize->end(), 872 | true); 873 | 874 | yomm11_class::add_to_initialize(&yomm11_class::of::the()); 875 | yomm11_class::add_to_initialize(&yomm11_class::of::the()); 876 | hierarchy_initializer::initialize(yomm11_class::of::the()); 877 | test(yomm11_class::to_initialize->empty() || 878 | find(yomm11_class::to_initialize->begin(), 879 | yomm11_class::to_initialize->end(), 880 | &yomm11_class::of::the()) == yomm11_class::to_initialize->end(), 881 | true); 882 | test(yomm11_class::to_initialize->empty() || 883 | find(yomm11_class::to_initialize->begin(), 884 | yomm11_class::to_initialize->end(), 885 | &yomm11_class::of::the()) == yomm11_class::to_initialize->end(), 886 | true); 887 | } 888 | 889 | cout << "\n--- Grouping resolver tests\n"; 890 | 891 | { 892 | using namespace grouping_resolver_tests; 893 | 894 | // we want to build: 895 | // Interface Terminal Window+ Mobile+ 896 | // Animal 0 0 0 mob 897 | // Herbivore+ 0 p_herb d_herb mob 898 | // Carnivore+ 0 p_carn d_carn mob 899 | 900 | hierarchy_initializer::initialize(yomm11_class::of::the()); 901 | hierarchy_initializer::initialize(yomm11_class::of::the()); 902 | 903 | grouping_resolver rdisp(display.the()); 904 | 905 | methods animal_applicable; 906 | rdisp.find_applicable(0, &yomm11_class::of::the(), animal_applicable); 907 | test( animal_applicable, methods { display.the().methods[4] } ); 908 | 909 | methods herbivore_applicable; 910 | rdisp.find_applicable(0, &yomm11_class::of::the(), herbivore_applicable); 911 | test( herbivore_applicable, (methods { display.the().methods[0], display.the().methods[1], display.the().methods[4] } )); 912 | 913 | methods cow_applicable; 914 | rdisp.find_applicable(0, &yomm11_class::of::the(), cow_applicable); 915 | test( cow_applicable, (methods { display.the().methods[0], display.the().methods[1], display.the().methods[4] } )); 916 | 917 | methods carnivore_applicable; 918 | rdisp.find_applicable(0, &yomm11_class::of::the(), carnivore_applicable); 919 | test( carnivore_applicable, (methods { display.the().methods[2], display.the().methods[3], display.the().methods[4] }) ); 920 | 921 | methods wolf_applicable; 922 | rdisp.find_applicable(0, &yomm11_class::of::the(), wolf_applicable); 923 | test( wolf_applicable, (methods { display.the().methods[2], display.the().methods[3], display.the().methods[4] }) ); 924 | 925 | methods interface_applicable; 926 | rdisp.find_applicable(1, &yomm11_class::of::the(), interface_applicable); 927 | test( interface_applicable, methods { } ); 928 | 929 | methods terminal_applicable; 930 | rdisp.find_applicable(1, &yomm11_class::of::the(), terminal_applicable); 931 | test( terminal_applicable, (methods { display.the().methods[0], display.the().methods[2] }) ); 932 | 933 | methods window_applicable; 934 | rdisp.find_applicable(1, &yomm11_class::of::the(), window_applicable); 935 | test( window_applicable, (methods { display.the().methods[1], display.the().methods[3] }) ); 936 | 937 | methods mobile_applicable; 938 | rdisp.find_applicable(1, &yomm11_class::of::the(), mobile_applicable); 939 | test( mobile_applicable, (methods { display.the().methods[4] }) ); 940 | 941 | // Animal = class_0 942 | // Herbivore = class_1 943 | // Cow = class_2 944 | // Carnivore = class_3 945 | // Wolf = class_4 946 | // Tiger = class_5 947 | 948 | // Interface = class_0 949 | // Terminal = class_1 950 | // Window = class_2 951 | // MsWindows = class_3 952 | // X = class_4 953 | // Mobile = class_5 954 | // Nokia = class_6 955 | // Samsung = class_7 956 | 957 | rdisp.make_groups(); 958 | test( rdisp.groups.size(), 2 ) && 959 | test( rdisp.groups[0].size(), 3) && 960 | test( rdisp.groups[0][0].methods, animal_applicable) && 961 | test( rdisp.groups[0][1].methods, herbivore_applicable) && 962 | test( rdisp.groups[0][2].methods, carnivore_applicable) && 963 | test( rdisp.groups[1].size(), 4) && 964 | test( rdisp.groups[1][0].methods, interface_applicable) && 965 | test( rdisp.groups[1][1].methods, terminal_applicable) && 966 | test( rdisp.groups[1][2].methods, window_applicable) && 967 | test( rdisp.groups[1][3].methods, mobile_applicable); 968 | 969 | test(display.the().steps.size(), 2) && 970 | test(display.the().steps[0], 1) && 971 | test(display.the().steps[1], 3); 972 | 973 | test( (*Animal()._yomm11_ptbl)[0].index, 0 ); 974 | test( (*Herbivore()._yomm11_ptbl)[0].index, 1 ); 975 | test( (*Cow()._yomm11_ptbl)[0].index, 1 ); 976 | test( (*Carnivore()._yomm11_ptbl)[0].index, 2 ); 977 | test( (*Wolf()._yomm11_ptbl)[0].index, 2 ); 978 | test( (*Tiger()._yomm11_ptbl)[0].index, 2 ); 979 | test( (*Interface()._yomm11_ptbl).size(), 1 ); 980 | test( (*Interface()._yomm11_ptbl)[0].index, 0 ); 981 | test( (*Terminal()._yomm11_ptbl)[0].index, 1 ); 982 | test( (*Window()._yomm11_ptbl)[0].index, 2 ); 983 | test( (*MSWindows()._yomm11_ptbl)[0].index, 2 ); 984 | test( (*X()._yomm11_ptbl)[0].index, 2 ); 985 | test( (*Mobile()._yomm11_ptbl)[0].index, 3 ); 986 | test( (*Nokia()._yomm11_ptbl)[0].index, 3 ); 987 | test( (*Samsung()._yomm11_ptbl)[0].index, 3 ); 988 | 989 | rdisp.make_table(); 990 | auto table = display.the().dispatch_table; 991 | auto methods = display.the().methods; 992 | using method = decltype(display)::method_entry; 993 | 994 | test( table != 0, true); 995 | // Interface 996 | test( table[0], throw_undefined::body ); 997 | test( table[1], throw_undefined::body ); 998 | test( table[2], throw_undefined::body ); 999 | 1000 | // Terminal 1001 | test( table[3], throw_undefined::body ); 1002 | test( table[4], static_cast(methods[0])->pm ); 1003 | test( table[5], static_cast(methods[2])->pm ); 1004 | 1005 | // Window 1006 | test( table[6], throw_undefined::body ); 1007 | test( table[7], static_cast(methods[1])->pm ); 1008 | test( table[8], static_cast(methods[3])->pm ); 1009 | 1010 | // Mobile 1011 | test( table[9], static_cast(methods[4])->pm ); 1012 | test( table[10], static_cast(methods[4])->pm ); 1013 | test( table[11], static_cast(methods[4])->pm ); 1014 | 1015 | rdisp.assign_next(); 1016 | test( (display_specialization::next) == nullptr, true ); 1017 | 1018 | testx( (void*) yomm11_class::of::the().mmt[0].ptr, 1019 | (void*) display.impl->dispatch_table ); 1020 | 1021 | testx( (void*) yomm11_class::of::the().mmt[0].ptr, 1022 | (void*) (display.impl->dispatch_table + 1) ); 1023 | 1024 | test( display(Herbivore(), Terminal()), print_herbivore ); 1025 | test( display(Cow(), Terminal()), print_herbivore ); 1026 | 1027 | test( display(Carnivore(), Terminal()), print_carnivore ); 1028 | test( display(Wolf(), Terminal()), print_carnivore ); 1029 | test( display(Tiger(), Terminal()), print_carnivore ); 1030 | 1031 | test( display(Herbivore(), Window()), draw_herbivore ); 1032 | test( display(Cow(), Window()), draw_herbivore ); 1033 | test( display(Cow(), MSWindows()), draw_herbivore ); 1034 | test( display(Cow(), X()), draw_herbivore ); 1035 | 1036 | test( display(Carnivore(), Window()), draw_carnivore ); 1037 | test( display(Wolf(), X()), draw_carnivore ); 1038 | test( display(Tiger(), MSWindows()), draw_carnivore ); 1039 | 1040 | test( display(Herbivore(), Samsung()), mobile ); 1041 | test( display(Cow(), Nokia()), mobile ); 1042 | 1043 | test( display(Carnivore(), Samsung()), mobile ); 1044 | test( display(Wolf(), Nokia()), mobile ); 1045 | 1046 | test( decltype(display)::resolve(Wolf(), Nokia()), mobile ); 1047 | } 1048 | 1049 | cout << "\n--- Single inheritance." << endl; 1050 | 1051 | { 1052 | using namespace single_inheritance; 1053 | 1054 | Cow c; 1055 | Wolf w; 1056 | Tiger t; 1057 | Terminal term; 1058 | Window win; 1059 | Interface interf; 1060 | Herbivore herb; 1061 | 1062 | yorel::methods::initialize(); 1063 | 1064 | test(encounter.impl != nullptr, true); 1065 | test(encounter.impl->dispatch_table != nullptr, true); 1066 | test(encounter(c, w), "run"); 1067 | test(encounter(c, c), "ignore"); 1068 | 1069 | // static call 1070 | test(GET_SPECIALIZATION(encounter, string, Animal&, Animal&)(c, w), "ignore"); 1071 | 1072 | // next 1073 | test(encounter_specialization::next(w, w), "fight"); 1074 | test(encounter_specialization::next(w, w), "hunt"); 1075 | test(encounter_specialization::next(w, w), "ignore"); 1076 | } 1077 | 1078 | cout << "\n--- multiple inheritance" << endl; 1079 | 1080 | { 1081 | using namespace mi; 1082 | 1083 | Animal animal; 1084 | Herbivore herbivore; 1085 | Stallion stallion; 1086 | Mare mare; 1087 | Wolf wolf; 1088 | 1089 | static_assert(is_virtual_base_of::value, "problem with virtual base detection"); 1090 | 1091 | yorel::methods::initialize(); 1092 | 1093 | testx( (void*) yomm11_class::of::the().mmt[0].ptr, 1094 | (void*) encounter.impl->dispatch_table ); 1095 | 1096 | testx( (void*) yomm11_class::of::the().mmt[0].ptr, 1097 | (void*) (encounter.impl->dispatch_table) ); 1098 | 1099 | testx( (void*) yomm11_class::of::the().mmt[0].ptr, 1100 | (void*) (encounter.impl->dispatch_table + 1) ); 1101 | 1102 | test( encounter(animal, animal), "ignore" ); 1103 | test( encounter(herbivore, herbivore), "ignore" ); 1104 | test( encounter(stallion, mare), "court" ); 1105 | test( encounter(mare, mare), "ignore" ); 1106 | test( encounter(wolf, mare), "hunt" ); 1107 | } 1108 | 1109 | { 1110 | cout << "\n--- adjustments." << endl; 1111 | using namespace adjust; 1112 | 1113 | A a; 1114 | a.val = 2; 1115 | B b; 1116 | b.val = 5; 1117 | X& xa = a; 1118 | X& xb = b; 1119 | 1120 | test( (&cast::value(a)), &a); 1121 | test( (&cast::value(xb)), &b); 1122 | test( (&cast::value(xb)), &b); 1123 | 1124 | test( (void*) &b != (void*) (X*) &a, true ); 1125 | 1126 | test( foo(a, a), 4 ); 1127 | test( foo(a, b), -3 ); 1128 | test( foo(b, b), 25 ); 1129 | } 1130 | 1131 | { 1132 | cout << "\n--- adjustments." << endl; 1133 | using namespace adjust_virtual; 1134 | 1135 | A a; 1136 | a.val = 2; 1137 | B b; 1138 | b.val = 5; 1139 | 1140 | test( (void*) &a != (void*) (X*) &a, true ); 1141 | test( (void*) &b != (void*) (X*) &a, true ); 1142 | 1143 | test( foo(a, a), 4 ); 1144 | test( foo(a, b), -3 ); 1145 | test( foo(b, b), 25 ); 1146 | } 1147 | 1148 | { 1149 | cout << "\n--- Unloading methods." << endl; 1150 | using namespace single_inheritance; 1151 | 1152 | encounter.the().add_spec>(); 1153 | 1154 | test( yomm11_class::of::the().rooted_here.size(), 3 ); 1155 | test( yomm11_class::of::the().rooted_here.size(), 1 ); 1156 | test( method_base::to_initialize != nullptr, true ); 1157 | test( method_base::to_initialize->size(), 1 ); 1158 | 1159 | delete encounter.impl; 1160 | encounter.impl = nullptr; 1161 | test( yomm11_class::of::the().rooted_here.size(), 1 ); 1162 | test( !method_base::to_initialize, true ); 1163 | 1164 | cout << "\n--- Unloading classes." << endl; 1165 | { 1166 | // fake a class 1167 | yomm11_class donkey_class YOMM11_TRACE(("Donkey")); 1168 | donkey_class.initialize(yomm11_class_vector_of::get()); 1169 | test( yomm11_class::to_initialize != nullptr, true ); 1170 | test( yomm11_class::to_initialize->size(), 1 ); 1171 | yorel::methods::initialize(); 1172 | test( !yomm11_class::to_initialize, true ); 1173 | } 1174 | 1175 | test( yomm11_class::to_initialize != nullptr, true ); 1176 | test( yomm11_class::to_initialize->size(), 1 ); 1177 | yorel::methods::initialize(); 1178 | test( !yomm11_class::to_initialize, true ); 1179 | } 1180 | 1181 | { 1182 | cout << "\n--- Adjustments." << endl; 1183 | using namespace adjust; 1184 | 1185 | A a; 1186 | a.val = 2; 1187 | B b; 1188 | b.val = 5; 1189 | 1190 | test( (void*) &b != (void*) (X*) &a, true ); 1191 | 1192 | test( foo(a, a), 4 ); 1193 | test( foo(a, b), -3 ); 1194 | test( foo(b, b), 25 ); 1195 | } 1196 | 1197 | { 1198 | cout << "\n--- Adjustments - virtual." << endl; 1199 | using namespace adjust_virtual; 1200 | 1201 | A a; 1202 | a.val = 2; 1203 | B b; 1204 | b.val = 5; 1205 | 1206 | test( (void*) &a != (void*) (X*) &a, true ); 1207 | test( (void*) &b != (void*) (X*) &a, true ); 1208 | 1209 | test( foo(a, a), 4 ); 1210 | test( foo(a, b), -3 ); 1211 | test( foo(b, b), 25 ); 1212 | } 1213 | 1214 | { 1215 | cout << "\n--- Multiple roots." << endl; 1216 | using namespace multi_roots; 1217 | 1218 | XY xy; 1219 | xy.x = 1; 1220 | xy.y = 2; 1221 | 1222 | test( xy.X::_yomm11_ptbl == xy.Y::_yomm11_ptbl, true ); 1223 | 1224 | test( mx(xy), 1 ); 1225 | test( my(xy), 2 ); 1226 | test( mxy(xy), 3 ); 1227 | } 1228 | 1229 | { 1230 | cout << "\n--- Multiple roots - foreign." << endl; 1231 | using namespace multi_roots_foreign; 1232 | 1233 | XY xy; 1234 | xy.x = 1; 1235 | xy.y = 2; 1236 | 1237 | test( mx(xy), 1 ); 1238 | test( my(xy), 2 ); 1239 | test( mxy(xy), 3 ); 1240 | } 1241 | 1242 | { 1243 | cout << "\n--- Repeated." << endl; 1244 | using namespace repeated; 1245 | 1246 | AB ab; 1247 | ab.A::x = 2; 1248 | ab.B::x = 3; 1249 | ab.a = 5; 1250 | ab.b = 7; 1251 | A& a = ab; 1252 | B& b = ab; 1253 | 1254 | test( ab.A::_yomm11_ptbl == ab.B::_yomm11_ptbl, true ); 1255 | 1256 | test( ma(ab), 7 ); 1257 | test( mb(ab), 10 ); 1258 | test( mab(ab), 17 ); 1259 | test( mx(a), 17 ); 1260 | test( mx(b), 17 ); 1261 | } 1262 | 1263 | cout << "\n" << success << " tests succeeded, " << failure << " failed.\n"; 1264 | 1265 | return 0; 1266 | } 1267 | -------------------------------------------------------------------------------- /tests/util/animals_mi.hpp: -------------------------------------------------------------------------------- 1 | 2 | struct Animal : mm { 3 | Animal() { 4 | mminit(); 5 | } 6 | }; 7 | 8 | struct Carnivore : virtual Animal { 9 | Carnivore() { 10 | mminit(); 11 | } 12 | }; 13 | 14 | struct Herbivore : virtual Animal { 15 | Herbivore() { 16 | mminit(); 17 | } 18 | }; 19 | 20 | struct Mammal : virtual Animal { 21 | Mammal() { 22 | mminit(); 23 | } 24 | }; 25 | 26 | struct Cow : Mammal, Herbivore { 27 | Cow() { 28 | mminit(); 29 | } 30 | }; 31 | 32 | struct Wolf : Mammal, Carnivore { 33 | Wolf() { 34 | mminit(); 35 | } 36 | }; 37 | 38 | #define show(e) #e << " = " << (e) 39 | -------------------------------------------------------------------------------- /tests/util/join.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JOIN_MANIP_HPP 2 | #define JOIN_MANIP_HPP 3 | 4 | #include 5 | #include 6 | 7 | template 8 | struct join_manip { 9 | join_manip(const std::string& sep, Expr first, Exprs... others) : sep(sep), first(first), others(sep, others...) { } 10 | const std::string& sep; 11 | typename std::conditional< 12 | std::is_trivial::value, 13 | Expr, 14 | const Expr&>::type first; 15 | join_manip others; 16 | }; 17 | 18 | template 19 | struct join_manip { 20 | join_manip(const std::string& sep, Expr only) : sep(sep), only(only) { } 21 | const std::string& sep; 22 | typename std::conditional< 23 | std::is_trivial::value, 24 | Expr, 25 | const Expr&>::type only; 26 | }; 27 | 28 | template 29 | join_manip join(const std::string& sep, Exprs... args) { 30 | return join_manip(sep, args...); 31 | } 32 | 33 | template 34 | std::ostream& operator <<(std::ostream& os, const join_manip& m) { 35 | return os << m.first << m.sep << m.others; 36 | } 37 | 38 | template 39 | std::ostream& operator <<(std::ostream& os, const join_manip& m) { 40 | return os << m.only; 41 | } 42 | 43 | #endif 44 | --------------------------------------------------------------------------------