mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 04:22:12 +00:00
doc
This commit is contained in:
@@ -26,8 +26,17 @@ foreach (cpp ${cpp_files})
|
||||
add_dependencies(tests ${stem})
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(headers_namespaces)
|
||||
|
||||
if (NOT WIN32)
|
||||
add_subdirectory(shared_libs)
|
||||
endif()
|
||||
|
||||
file(GLOB subdirs "rolex/*")
|
||||
|
||||
foreach (subdir ${subdirs})
|
||||
string(REGEX REPLACE ".*/" "" subex ${subdir})
|
||||
file(GLOB cpp_files "${subdir}/*.cpp")
|
||||
add_executable("rolex_${subex}" ${cpp_files})
|
||||
target_link_libraries("rolex_${subex}" PUBLIC Boost::openmethod)
|
||||
add_test(NAME "rolex_${subex}" COMMAND "rolex_${subex}")
|
||||
add_dependencies(tests "rolex_${subex}")
|
||||
endforeach()
|
||||
|
||||
@@ -29,6 +29,12 @@ struct Times : Node {
|
||||
const Node& left; const Node& right;
|
||||
};
|
||||
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// library code
|
||||
// =============================================================================
|
||||
// application code
|
||||
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
#include <iostream>
|
||||
@@ -67,6 +73,7 @@ int main() {
|
||||
postfix(e, std::cout);
|
||||
std::cout << " = " << e.value() << "\n"; // 2 3 + 4 * = 20
|
||||
}
|
||||
// end::content[]
|
||||
|
||||
void call_via_ref(const Node& node, std::ostream& os) {
|
||||
postfix(node, os);
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
|
||||
using namespace boost::openmethod::aliases;
|
||||
|
||||
// NOTE: unique_virtual_ptr<Class> is an alias for
|
||||
// virtual_ptr<std::unique_ptr<Class>>
|
||||
|
||||
struct Node {
|
||||
virtual ~Node() {}
|
||||
virtual int value() const = 0;
|
||||
@@ -70,11 +67,13 @@ BOOST_OPENMETHOD_CLASSES(Node, Variable, Plus, Times);
|
||||
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
auto a = std::make_unique<Variable>(2);
|
||||
auto b = std::make_unique<Variable>(3);
|
||||
auto c = std::make_unique<Variable>(4);
|
||||
auto d = make_unique_virtual<Plus>(std::move(a), std::move(b));
|
||||
auto e = make_unique_virtual<Times>(std::move(d), std::move(c));
|
||||
|
||||
postfix(e, std::cout);
|
||||
std::cout << " = " << e->value() << "\n"; // 2 3 + 4 * = 20
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class Animal {
|
||||
class Cat;
|
||||
class Dog;
|
||||
|
||||
template<typename> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
|
||||
template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
|
||||
|
||||
class Animal {
|
||||
// ...
|
||||
|
||||
@@ -43,7 +43,7 @@ BOOST_OPENMETHOD(poke, (std::ostream&, virtual_ptr<Animal>), void);
|
||||
namespace pets {
|
||||
struct Cat;
|
||||
struct Dog;
|
||||
template<typename> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
|
||||
template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
|
||||
} // namespace pets
|
||||
|
||||
namespace core {
|
||||
|
||||
16
doc/modules/ROOT/examples/rolex/1/employee.cpp
Normal file
16
doc/modules/ROOT/examples/rolex/1/employee.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
|
||||
return 5000.0;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee)
|
||||
// end::content[]
|
||||
23
doc/modules/ROOT/examples/rolex/1/main.cpp
Normal file
23
doc/modules/ROOT/examples/rolex/1/main.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// main.cpp
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
Employee bill;
|
||||
Salesman bob; bob.sales = 100'000.0;
|
||||
|
||||
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
|
||||
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
|
||||
}
|
||||
// end::content[]
|
||||
23
doc/modules/ROOT/examples/rolex/1/roles.hpp
Normal file
23
doc/modules/ROOT/examples/rolex/1/roles.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// roles.hpp
|
||||
|
||||
#ifndef ROLES_HPP
|
||||
#define ROLES_HPP
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
struct Employee { virtual ~Employee() = default; };
|
||||
|
||||
struct Salesman : Employee {
|
||||
double sales = 0.0;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
|
||||
|
||||
#endif // ROLES_HPP
|
||||
// end::content[]
|
||||
17
doc/modules/ROOT/examples/rolex/1/salesman.cpp
Normal file
17
doc/modules/ROOT/examples/rolex/1/salesman.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
|
||||
return next(emp) + emp->sales * 0.05; // base + commission
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
|
||||
// end::content[]
|
||||
16
doc/modules/ROOT/examples/rolex/2/employee.cpp
Normal file
16
doc/modules/ROOT/examples/rolex/2/employee.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_DEFINE_OVERRIDER(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
|
||||
return 5000.0;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee)
|
||||
// end::content[]
|
||||
23
doc/modules/ROOT/examples/rolex/2/main.cpp
Normal file
23
doc/modules/ROOT/examples/rolex/2/main.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// main.cpp
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
Employee bill;
|
||||
Salesman bob; bob.sales = 100'000.0;
|
||||
|
||||
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
|
||||
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
|
||||
}
|
||||
// end::content[]
|
||||
27
doc/modules/ROOT/examples/rolex/2/roles.hpp
Normal file
27
doc/modules/ROOT/examples/rolex/2/roles.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// roles.hpp
|
||||
|
||||
#ifndef ROLES_HPP
|
||||
#define ROLES_HPP
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
struct Employee { virtual ~Employee() = default; };
|
||||
|
||||
struct Salesman : Employee {
|
||||
double sales = 0.0;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
|
||||
|
||||
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee>), double);
|
||||
|
||||
|
||||
#endif // ROLES_HPP
|
||||
// end::content[]
|
||||
19
doc/modules/ROOT/examples/rolex/2/salesman.cpp
Normal file
19
doc/modules/ROOT/examples/rolex/2/salesman.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
// tag::content[]
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
|
||||
return BOOST_OPENMETHOD_OVERRIDER(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee> emp),
|
||||
double)::fn(emp) +
|
||||
emp->sales * 0.05; // base + commission
|
||||
}
|
||||
// end::content[]
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
11
doc/modules/ROOT/examples/rolex/3/employee.cpp
Normal file
11
doc/modules/ROOT/examples/rolex/3/employee.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee)
|
||||
// end::content[]
|
||||
23
doc/modules/ROOT/examples/rolex/3/main.cpp
Normal file
23
doc/modules/ROOT/examples/rolex/3/main.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// main.cpp
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
Employee bill;
|
||||
Salesman bob; bob.sales = 100'000.0;
|
||||
|
||||
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
|
||||
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
|
||||
}
|
||||
// end::content[]
|
||||
28
doc/modules/ROOT/examples/rolex/3/roles.hpp
Normal file
28
doc/modules/ROOT/examples/rolex/3/roles.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// roles.hpp
|
||||
|
||||
#ifndef ROLES_HPP
|
||||
#define ROLES_HPP
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
struct Employee { virtual ~Employee() = default; };
|
||||
|
||||
struct Salesman : Employee {
|
||||
double sales = 0.0;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
|
||||
|
||||
BOOST_OPENMETHOD_INLINE_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
|
||||
return 5000.0;
|
||||
}
|
||||
|
||||
#endif // ROLES_HPP
|
||||
// end::content[]
|
||||
19
doc/modules/ROOT/examples/rolex/3/salesman.cpp
Normal file
19
doc/modules/ROOT/examples/rolex/3/salesman.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
// tag::content[]
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
|
||||
return BOOST_OPENMETHOD_OVERRIDER(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee> emp),
|
||||
double)::fn(emp) +
|
||||
emp->sales * 0.05; // base + commission
|
||||
}
|
||||
// end::content[]
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
11
doc/modules/ROOT/examples/rolex/4/employee.cpp
Normal file
11
doc/modules/ROOT/examples/rolex/4/employee.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(employees::Employee)
|
||||
// end::content[]
|
||||
23
doc/modules/ROOT/examples/rolex/4/main.cpp
Normal file
23
doc/modules/ROOT/examples/rolex/4/main.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// main.cpp
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
employees::Employee bill;
|
||||
sales::Salesman bob; bob.sales = 100'000.0;
|
||||
|
||||
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
|
||||
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
|
||||
}
|
||||
// end::content[]
|
||||
37
doc/modules/ROOT/examples/rolex/4/roles.hpp
Normal file
37
doc/modules/ROOT/examples/rolex/4/roles.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// roles.hpp
|
||||
|
||||
#ifndef ROLES_HPP
|
||||
#define ROLES_HPP
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
namespace employees {
|
||||
|
||||
struct Employee {
|
||||
virtual ~Employee() = default;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
|
||||
|
||||
BOOST_OPENMETHOD_INLINE_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
|
||||
return 5000.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sales {
|
||||
|
||||
struct Salesman : employees::Employee {
|
||||
double sales = 0.0;
|
||||
};
|
||||
|
||||
} // namespace sales
|
||||
#endif // ROLES_HPP
|
||||
// end::content[]
|
||||
23
doc/modules/ROOT/examples/rolex/4/salesman.cpp
Normal file
23
doc/modules/ROOT/examples/rolex/4/salesman.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
// tag::content[]
|
||||
namespace sales {
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
|
||||
return employees::BOOST_OPENMETHOD_OVERRIDER(
|
||||
pay, (boost::openmethod::virtual_ptr<const employees::Employee> emp),
|
||||
double)::fn(emp) +
|
||||
emp->sales * 0.05; // base + commission
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(employees::Employee, Salesman)
|
||||
|
||||
} // namespace sales
|
||||
// end::content[]
|
||||
90
doc/modules/ROOT/examples/rolex/5/main.cpp
Normal file
90
doc/modules/ROOT/examples/rolex/5/main.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
#include <iostream>
|
||||
|
||||
class Payroll;
|
||||
|
||||
struct Employee {
|
||||
virtual ~Employee() = default;
|
||||
};
|
||||
|
||||
struct Salesman : Employee {
|
||||
double sales = 0.0;
|
||||
};
|
||||
|
||||
// tag::pay[]
|
||||
BOOST_OPENMETHOD(
|
||||
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
|
||||
double);
|
||||
// end::pay[]
|
||||
|
||||
// tag::payroll[]
|
||||
class Payroll {
|
||||
public:
|
||||
double balance() const {
|
||||
return balance_;
|
||||
}
|
||||
|
||||
private:
|
||||
double balance_ = 1'000'000.0;
|
||||
|
||||
void update_balance(double amount) {
|
||||
// throw if balance would become negative
|
||||
balance_ += amount;
|
||||
}
|
||||
|
||||
friend BOOST_OPENMETHOD_OVERRIDER(
|
||||
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
|
||||
double);
|
||||
friend BOOST_OPENMETHOD_OVERRIDER(
|
||||
pay,
|
||||
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman>),
|
||||
double);
|
||||
};
|
||||
// end::payroll[]
|
||||
|
||||
// tag::overriders[]
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
|
||||
double) {
|
||||
double pay = 5000.0;
|
||||
payroll.update_balance(-pay);
|
||||
|
||||
return pay;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay,
|
||||
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman> emp),
|
||||
double) {
|
||||
double base = next(payroll, emp);
|
||||
double commission = emp->sales * 0.05;
|
||||
payroll.update_balance(-commission);
|
||||
|
||||
return base + commission;
|
||||
}
|
||||
|
||||
// ...and let's not forget to register the classes
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
// end::overriders[]
|
||||
|
||||
// tag::main[]
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
Payroll payroll;
|
||||
Employee bill;
|
||||
Salesman bob;
|
||||
bob.sales = 100'000.0;
|
||||
|
||||
std::cout << "pay bill: $" << pay(payroll, bill) << "\n"; // $5000
|
||||
std::cout << "pay bob: $" << pay(payroll, bob) << "\n"; // 10000
|
||||
std::cout << "remaining balance: $" << payroll.balance() << "\n"; // $985000
|
||||
}
|
||||
// end::main[]
|
||||
85
doc/modules/ROOT/examples/rolex/6/main.cpp
Normal file
85
doc/modules/ROOT/examples/rolex/6/main.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
#include <iostream>
|
||||
|
||||
class Payroll;
|
||||
|
||||
struct Employee {
|
||||
virtual ~Employee() = default;
|
||||
};
|
||||
|
||||
struct Salesman : Employee {
|
||||
double sales = 0.0;
|
||||
};
|
||||
|
||||
// tag::pay[]
|
||||
BOOST_OPENMETHOD(
|
||||
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
|
||||
double);
|
||||
// end::pay[]
|
||||
|
||||
// tag::payroll[]
|
||||
class Payroll {
|
||||
public:
|
||||
double balance() const {
|
||||
return balance_;
|
||||
}
|
||||
|
||||
private:
|
||||
double balance_ = 1'000'000.0;
|
||||
|
||||
void update_balance(double amount) {
|
||||
// throw if balance would become negative
|
||||
balance_ += amount;
|
||||
}
|
||||
|
||||
template<typename...>
|
||||
friend struct BOOST_OPENMETHOD_OVERRIDERS(pay);
|
||||
};
|
||||
// end::payroll[]
|
||||
|
||||
// tag::overriders[]
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
|
||||
double) {
|
||||
double pay = 5000.0;
|
||||
payroll.update_balance(-pay);
|
||||
|
||||
return pay;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
pay,
|
||||
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman> emp),
|
||||
double) {
|
||||
double base = next(payroll, emp);
|
||||
double commission = emp->sales * 0.05;
|
||||
payroll.update_balance(-commission);
|
||||
|
||||
return base + commission;
|
||||
}
|
||||
|
||||
// ...and let's not forget to register the classes
|
||||
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
|
||||
// end::overriders[]
|
||||
|
||||
// tag::main[]
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
Payroll payroll;
|
||||
Employee bill;
|
||||
Salesman bob;
|
||||
bob.sales = 100'000.0;
|
||||
|
||||
std::cout << "pay bill: $" << pay(payroll, bill) << "\n"; // $5000
|
||||
std::cout << "pay bob: $" << pay(payroll, bob) << "\n"; // 10000
|
||||
std::cout << "remaining balance: $" << payroll.balance() << "\n"; // $985000
|
||||
}
|
||||
// end::main[]
|
||||
90
doc/modules/ROOT/examples/rolex/7/main.cpp
Normal file
90
doc/modules/ROOT/examples/rolex/7/main.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2018-2025 Jean-Louis Leroy
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/initialize.hpp>
|
||||
#include <iostream>
|
||||
|
||||
// tag::classes[]
|
||||
struct Role {
|
||||
virtual ~Role() = default;
|
||||
};
|
||||
|
||||
struct Employee : Role {};
|
||||
struct Salesman : Employee {};
|
||||
struct Manager : Employee {};
|
||||
struct Founder : Role {};
|
||||
|
||||
struct Expense {
|
||||
virtual ~Expense() {
|
||||
}
|
||||
};
|
||||
|
||||
struct Public : Expense {};
|
||||
struct Bus : Public {};
|
||||
struct Metro : Public {};
|
||||
struct Taxi : Expense {};
|
||||
struct Jet : Expense {};
|
||||
// end::classes[]
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(
|
||||
Role, Employee, Manager, Founder, Expense, Public, Bus, Metro, Taxi, Jet);
|
||||
|
||||
// tag::approve[]
|
||||
using boost::openmethod::virtual_ptr;
|
||||
|
||||
BOOST_OPENMETHOD(
|
||||
approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
|
||||
bool);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
|
||||
bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
approve, (virtual_ptr<const Employee>, virtual_ptr<const Public>, double),
|
||||
bool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
approve,
|
||||
(virtual_ptr<const Manager>, virtual_ptr<const Taxi>, double amount),
|
||||
bool) {
|
||||
return amount < 100.0;
|
||||
}
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
approve, (virtual_ptr<const Founder>, virtual_ptr<const Expense>, double),
|
||||
bool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// tag::main[]
|
||||
int main() {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
Founder bill;
|
||||
Employee bob;
|
||||
Manager alice;
|
||||
|
||||
Bus bus;
|
||||
Taxi taxi;
|
||||
Jet jet;
|
||||
|
||||
std::cout << std::boolalpha;
|
||||
std::cout << approve(bill, bus, 4.0) << "\n"; // true
|
||||
std::cout << approve(bob, bus, 4.0) << "\n"; // true
|
||||
std::cout << approve(bob, taxi, 36.0) << "\n"; // false
|
||||
std::cout << approve(alice, taxi, 36.0) << "\n"; // true
|
||||
std::cout << approve(alice, taxi, 2000.0) << "\n"; // false
|
||||
std::cout << approve(bill, jet, 120'000.0) << "\n"; // true
|
||||
std::cout << approve(bob, jet, 120'000.0) << "\n"; // false
|
||||
std::cout << approve(alice, jet, 120'000.0) << "\n"; // false
|
||||
}
|
||||
// end::main[]
|
||||
@@ -2,7 +2,8 @@
|
||||
* xref:basics.adoc[Basics]
|
||||
* xref:performance.adoc[Performance]
|
||||
* xref:smart_pointers.adoc[Smart Pointers]
|
||||
* xref:headers_namespaces.adoc[Headers and Namespaces]
|
||||
* xref:headers.adoc[Headers]
|
||||
* xref:namespaces.adoc[Namespaces]
|
||||
* xref:friendship.adoc[Friendship]
|
||||
* xref:multiple_dispatch.adoc[Multiple Dispatch]
|
||||
* Advanced Features
|
||||
@@ -13,6 +14,6 @@
|
||||
** xref:custom_rtti.adoc[Custom RTTI]
|
||||
** xref:shared_libraries.adoc[Shared Libraries]
|
||||
* Reference
|
||||
** xref:headers.adoc[Headers]
|
||||
** xref:macros.adoc[Macros]
|
||||
** xref:ref_headers.adoc[Headers]
|
||||
** xref:ref_macros.adoc[Macros]
|
||||
** xref:reference:boost/openmethod.adoc[Namespace boost::openmethod]
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
## Basics
|
||||
|
||||
An _open-method_ is a free-standing function that takes one or more _virtual_
|
||||
_parameters_. When it is called, it forwards to an _overrider_ selected by
|
||||
examining the dynamic type of the virtual parameters.
|
||||
_parameters_. When it is called, it forwards to an _overrider_ selected from a
|
||||
set by examining the dynamic type of the virtual parameters.
|
||||
|
||||
If this sounds like a virtual function, that's because because an open-method
|
||||
with one virtual parameter is equivalent to a virtual function - with a big
|
||||
@@ -69,8 +69,8 @@ constructors from plain references or pointers to an object, or from other
|
||||
|
||||
There are two more things we need to do.
|
||||
|
||||
OpenMethod is a library, not a compiler. It needs to be aware of all the classes
|
||||
that may be used as virtual parameters, and in method calls, and their
|
||||
OpenMethod is a library, not a compiler. It needs to be made aware of all the
|
||||
classes that may be used as virtual parameters, and in method calls, and their
|
||||
inheritance relationships. We do this using the
|
||||
xref:BOOST_OPENMETHOD_CLASSES.adoc[BOOST_OPENMETHOD_CLASSES] macro:
|
||||
|
||||
@@ -83,9 +83,9 @@ direct base of a class must appear together with it in at least one call to
|
||||
`BOOST_OPENMETHOD_CLASSES`. This enables the library to deduce the complete
|
||||
inheritance lattice.
|
||||
|
||||
The constructs used in this example require the classes need to be polymorphic,
|
||||
in the standard C++ sense, i.e. have at least one virtual function. The library
|
||||
can also be used with non-polymorphic classes, with some restrictions.
|
||||
The constructs used in this example require the classes to be polymorphic, in
|
||||
the standard C++ sense, i.e. have at least one virtual function. The library can
|
||||
also be used with non-polymorphic classes, with some restrictions.
|
||||
|
||||
Finally, we need to call `boost::openmethod::initialize()` before the first call
|
||||
to an open-method. This builds the dispatch tables used during method calls. It
|
||||
|
||||
@@ -1,31 +1,49 @@
|
||||
:example: ../examples/rolex
|
||||
|
||||
## Friendship
|
||||
|
||||
We can use overrider containers to grant friendship to a specific overrider, or
|
||||
to all the overriders of a method. The name of the container template is
|
||||
returned by `BOOST_OPENMETHOD_OVERRIDERS`. The template argument for a
|
||||
specialization is the signature of the overrider. For example, the overrider of
|
||||
`poke` for `Cat` is:
|
||||
Note;; This section uses overrider containers, described in the
|
||||
xref:headers.adoc[Headers] section.
|
||||
|
||||
`friend` is a controversial feature. OpenMethod aims to interact well with
|
||||
all of C++, as much as feasible for a library, and leave the user the choice of
|
||||
using `friend`, or not.
|
||||
|
||||
Let's consider yet another variation of the `pay` example. This time, we want to
|
||||
update a `balance` variable in a `Payroll` class, when an employee is paid. Thus
|
||||
we pass the payroll object to the `pay` method:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
BOOST_OPENMETHOD_OVERRIDERS(poke)<
|
||||
void(std::ostream& os, virtual_ptr<Cat> cat)>::fn;
|
||||
include::{example}/5/main.cpp[tag=pay]
|
||||
----
|
||||
|
||||
We can thus grant friendship to all the overriders of `poke`:
|
||||
`BOOST_OPENMETHOD` declares an overrider container for `pay` in the current
|
||||
namespace, even though it does not define any overrider by itself. We can thus
|
||||
name the individual address containers in `friend` declarations. But note that
|
||||
at this point, the containers have not been specialized yet! In particular, the
|
||||
`fn` member function does not exist yet. Instead, we declare friendship to the
|
||||
container itself:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$friendship.cpp[tag=friend_all]
|
||||
include::{example}/5/main.cpp[tag=payroll]
|
||||
----
|
||||
|
||||
Be aware, though, that the overriders of _any_ method called `poke` - with any
|
||||
signature - are granted friendship.
|
||||
|
||||
We can also befriend individual overriders:
|
||||
We can now implement the `pay` overriders:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$friendship.cpp[tag=friend]
|
||||
include::{example}/5/main.cpp[tag=overriders]
|
||||
----
|
||||
|
||||
We can also declare friendship _en_ _masse_:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/6/main.cpp[tag=payroll]
|
||||
----
|
||||
|
||||
Note, however, that this makes all the overriders of _any_ `pay` method, with
|
||||
any signature, in the current namespace, friends of `Payroll`. Unfortunately,
|
||||
C++ does not currently allow partial specialization of friend declarations.
|
||||
|
||||
@@ -1,111 +1,102 @@
|
||||
[#headers]
|
||||
= xref:headers.adoc[Headers]
|
||||
:example: ../examples/rolex
|
||||
|
||||
{empty}
|
||||
## Headers
|
||||
|
||||
## Headers for General Use
|
||||
Typically, `BOOST_OPENMETHOD` go in headers, while
|
||||
`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` go in
|
||||
implementation files.
|
||||
|
||||
The following headers are sufficient for most basic uses of the library.
|
||||
Let's use a payroll application as an example.
|
||||
We have two roles: `employee` and `salesman`, and a `pay` method that
|
||||
computes the monthly pay of an employee.
|
||||
We want to override and call `pay` from from multiple translation units, so
|
||||
we put it in a header:
|
||||
|
||||
* xref:#main[`boost/openmethod.hpp`] to define open-methods and overriders using
|
||||
convenient macros.
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/1/roles.hpp[tag=content]
|
||||
----
|
||||
|
||||
* xref:#initialize[`boost/openmethod/initialize.hpp`] to initialize the library.
|
||||
Typically only included in the translation unit containing `main`.
|
||||
`BOOST_OPENMETHOD` _defines_ an inline function, so it can be called only
|
||||
once in a translation unit.
|
||||
The include guards see to this.
|
||||
|
||||
The following headers make it possible to use standard smart pointers in virtual
|
||||
parameters:
|
||||
Let's write the override for "just" employees - they get a fixed salary.
|
||||
`BOOST_OPENMETHOD_OVERRIDE` _adds_ an overrider to the method.
|
||||
We don't want to add the same multiple times; that would create an
|
||||
ambiguity.
|
||||
Thus it should go in an implementation file:
|
||||
|
||||
* xref:#std_shared_ptr[`boost/openmethod/interop/std_shared_ptr.hpp`] to use
|
||||
`std::shared_ptr` in virtual parameters.
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/1/employee.cpp[tag=content]
|
||||
----
|
||||
|
||||
* xref:#std_unique_ptr[`boost/openmethod/interop/std_unique_ptr.hpp`] to use
|
||||
`std::unique_ptr` in virtual parameters.
|
||||
Salesmen get a salary like employees, and on top get a commission on their
|
||||
sales. The `next` function, available only in the body of an overrider, calls
|
||||
the next most specialized overrider. It is similar to `super` in other
|
||||
languages, except that it does not stand for an object, but for an overrider.
|
||||
|
||||
*The remaining headers are for advanced use*.
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/1/salesman.cpp[tag=content]
|
||||
----
|
||||
|
||||
## Pre-Core Headers
|
||||
Paytime!
|
||||
|
||||
The following headers can be included before `core.hpp` to define custom
|
||||
registries and policies, and override the default registry by defining
|
||||
xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[`BOOST_OPENMETHOD_DEFAULT_REGISTRY`].
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/1/main.cpp[tag=content]
|
||||
----
|
||||
|
||||
### boost/openmethod/preamble.hpp
|
||||
In the previous example, we used `next` to call the super-overrider. We can also
|
||||
call an overrider directly. To do this, we can declare the overrider in the
|
||||
header, and define it in an implementation file:
|
||||
|
||||
Defines `registry` and stock policy categories. Also defines all types and
|
||||
functions necessary for the definition of `registry`.
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/2/roles.hpp[tag=content]
|
||||
----
|
||||
|
||||
### boost/openmethod/policies/std_rtti.hpp
|
||||
Unlike function declarations,
|
||||
xref:BOOST_OPENMETHOD_DECLARE_OVERRIDER.adoc[BOOST_OPENMETHOD_DECLARE_OVERRIDER]
|
||||
cannot appear multiple times in a translation unit with the same arguments.
|
||||
Also, it requires the _method_ itself to be defined prior using this macro.
|
||||
|
||||
Provides an implementation of the `rtti` policy using standard RTTI.
|
||||
Overriders are placed in _overrider_ _containers_. An overrider container is a
|
||||
class template named after the method, declared in the current namespace. It is
|
||||
specialized for each overrider signature. Macro
|
||||
xref:BOOST_OPENMETHOD_OVERRIDER.adoc[BOOST_OPENMETHOD_OVERRIDER] takes the same
|
||||
arguments `BOOST_OPENMETHOD_OVERRIDE`, and expands to the corresponding
|
||||
specialization of the overrider container. Containers have a static member
|
||||
function `fn` that contains the body the overrider, provided by the user. We can
|
||||
call the overrider for `Employee` like so:
|
||||
|
||||
### boost/openmethod/policies/fast_perfect_hash.hpp
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/2/salesman.cpp[tag=content]
|
||||
----
|
||||
|
||||
Provides an implementation of the `hash` policy using a fast perfect hash
|
||||
function.
|
||||
This is similar to a virtual function calling a base overrider. Virtual
|
||||
functions don't have the equivalent of Smalltalk's or Python's `super`, but
|
||||
OpenMethod does, it's `next`. It is almost always the right choice.
|
||||
|
||||
### boost/openmethod/policies/vptr_vector.hpp
|
||||
The exception is: when performance is critical, we may want to inline the call
|
||||
to the base overrider.
|
||||
xref:BOOST_OPENMETHOD_INLINE_OVERRIDE.adoc[BOOST_OPENMETHOD_INLINE_OVERRIDE]
|
||||
defines the overrider as an inline function, and it can go in a header file:
|
||||
|
||||
Provides an implementation of the `vptr` policy that stores the v-table pointers
|
||||
in a `std::vector` indexed by type ids, possibly hashed.
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/3/roles.hpp[tag=content]
|
||||
----
|
||||
|
||||
### boost/openmethod/policies/default_error_handler.hpp
|
||||
With inlining the overrider for `Salesman` compiles to (clang-20, x64-linux):
|
||||
|
||||
Provides an implementation of the `error_handler` policy that calls a
|
||||
`std::function<void(openmethod_error)>` when an error is encountered, and before
|
||||
the library aborts the program.
|
||||
|
||||
### boost/openmethod/policies/stderr_output.hpp
|
||||
|
||||
Provides an implementation of the `output` policy that writes diagnostics to
|
||||
the C standard error stream (not using iostreams).
|
||||
|
||||
### boost/openmethod/default_registry.hpp
|
||||
|
||||
Defines the default registry, which contains all the stock policies listed
|
||||
above. Includes all the headers listed in the preamble section so far.
|
||||
|
||||
### boost/openmethod/policies/static_rtti.hpp
|
||||
|
||||
Provides a minimal implementation of the `rtti` policy that does not depend on
|
||||
standard RTTI.
|
||||
|
||||
### boost/openmethod/policies/throw_error_handler.hpp
|
||||
|
||||
Provides an implementation of the `error_handler` policy that throws errors as
|
||||
exceptions.
|
||||
|
||||
### boost/openmethod/policies/vptr_map.hpp
|
||||
|
||||
Provides an implementation of the `vptr` policy that stores the v-table pointers
|
||||
in a map (by default a `std::map`) indexed by type ids.
|
||||
|
||||
## High-level Headers
|
||||
|
||||
### boost/openmethod/core.hpp
|
||||
|
||||
Defines the main constructs of the library: methods, overriders and virtual
|
||||
pointers, and mechanisms to implement them. Does not define any public macros
|
||||
apart from `BOOST_OPENMETHOD_DEFAULT_REGISTRY`, if it is not defined already.
|
||||
|
||||
### boost/openmethod/macros.hpp
|
||||
|
||||
Defines the public macros of the library, such as `BOOST_OPENMETHOD`,
|
||||
`BOOST_OPENMETHOD_CLASSES`, etc.
|
||||
|
||||
There is little point in including this header directly, as this has the same
|
||||
effect as including `boost/openmethod.hpp`, which is shorter.
|
||||
|
||||
### boost/openmethod.hpp
|
||||
|
||||
Includes `core.hpp` and `macros.hpp`.
|
||||
|
||||
### boost/openmethod/interop/std_shared_ptr.hpp
|
||||
|
||||
Provides a `virtual_traits` specialization that make it possible to use a
|
||||
`std::shared_ptr` in place of a raw pointer or reference in virtual parameters.
|
||||
|
||||
### boost/openmethod/interop/std_unique_ptr.hpp
|
||||
|
||||
Provides a `virtual_traits` specialization that make it possible to use a
|
||||
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.
|
||||
[source,asm]
|
||||
----
|
||||
movsd xmm0, qword ptr [rsi + 8]
|
||||
mulsd xmm0, qword ptr [rip + .LCPI1_0]
|
||||
addsd xmm0, qword ptr [rip + .LCPI1_1]
|
||||
ret
|
||||
----
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
|
||||
## Headers and Namespaces
|
||||
|
||||
Most real-life programs will be organized in multiple files and multiple
|
||||
namespaces. OpenMethod interacts with headers and namespaces naturally, if
|
||||
using-directives are avoided. In that case, there are a few things to be aware
|
||||
of.
|
||||
|
||||
Let's break the Animals example into headers and namespaces. First we put
|
||||
`Animal` in its own header and namespace:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$headers_namespaces/animal.hpp[]
|
||||
----
|
||||
|
||||
`BOOST_OPENMETHOD` can be placed in a header file. It adds several constructs to
|
||||
the current namespace:
|
||||
|
||||
* It declares (but does not define) a `struct` named after the method.
|
||||
|
||||
* It declares (but does not define) a _guide_ function. It is also named after
|
||||
the method, and it has the same signature (with the `virtual_` decorators
|
||||
stripped). It is used to match methods and overriders. It is never defined and
|
||||
it is "called" only in a non-evaluated context.
|
||||
|
||||
* It defines an inline function with the same name and signature as the
|
||||
method (with the `virtual_` decorators stripped).
|
||||
|
||||
Next, let's implement the `Cat` class, and a derived class, `Cheetah`, in the
|
||||
`felines` namespace:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$headers_namespaces/cat.hpp[]
|
||||
----
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$headers_namespaces/cat.cpp[]
|
||||
----
|
||||
|
||||
`BOOST_OPENMETHOD_CLASSES` should be placed in an implementation file. It can
|
||||
also go in a header file, but this wastes space, as the same registrar will be
|
||||
created in every translation unit that includes the header. It doesn't matter
|
||||
which namespace the macro is called in. It can be used with any class name in
|
||||
scope, or with qualified names.
|
||||
|
||||
`BOOST_OPENMETHOD_OVERRIDE` uses the guide function declared by
|
||||
`BOOST_OPENMETHOD` to locate a method that can be called with the same arguments
|
||||
as the overrider itself. It "calls" the guide function in a non-evaluated
|
||||
context, passing it a `std::ostream&` and a `virtual_ptr<Cat>`. The return type
|
||||
of the guide function is the method to add the overrider to. Exactly one guide
|
||||
function must match. The normal rules of overload resolution apply. In that
|
||||
case, the guide function is found via argument dependant lookup (ADL).
|
||||
|
||||
The macro adds several constructs to the current namespace:
|
||||
|
||||
* It declares (but does not define) a `struct` template with one type parameter,
|
||||
named after the method. The template acts like a container for overriders.
|
||||
|
||||
* It specializes the template for the signature of the overrider. Inside the
|
||||
struct, it defines the `next` and `has_next` members, and a static function
|
||||
called `fn`. The block following the macro is the body of the `fn` function.
|
||||
|
||||
It follows that `BOOST_OPENMETHOD_OVERRIDE` should be placed in an
|
||||
implementation file. `BOOST_OPENMETHOD_INLINE_OVERRIDE` works like
|
||||
`BOOST_OPENMETHOD_OVERRIDE`, but it defines the `fn` function as inline, so it
|
||||
can be used in a header file.
|
||||
|
||||
The overrider for Cats can be accessed in the same translation unit, after it
|
||||
has been defined, using the `BOOST_OPENMETHOD_OVERRIDER` macro. It expands to
|
||||
the specialization of the overrider container for the overrider's signature. We
|
||||
call the static `fn` function to call the overrider.
|
||||
|
||||
NOTE: The Cheetah overrider calls the specific overrider for `Cat`, for
|
||||
illustration purpose. It is usually better to call `next` instead.
|
||||
|
||||
Let's implement the `Dog` class, in the `canines` namespace. This time we want
|
||||
the overrider to be accessible in other translation units. We can declare an
|
||||
overrider with `BOOST_OPENMETHOD_DECLARE_OVERRIDER`, without actually defining
|
||||
the static function `fn` just yet.
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$headers_namespaces/dog.hpp[]
|
||||
----
|
||||
|
||||
Unlike function declarations, which can occur multiple times in a TU, an
|
||||
overrider declaration cannot. For example, this is illegal:
|
||||
|
||||
```c++
|
||||
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
|
||||
poke, (std::ostream&, virtual_ptr<Dog>), void);
|
||||
|
||||
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
|
||||
poke, (std::ostream&, virtual_ptr<Dog>), void);
|
||||
```
|
||||
|
||||
Now we use `BOOST_OPENMETHOD_DEFINE_OVERRIDER` to define the overrider:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$headers_namespaces/dog.cpp[]
|
||||
----
|
||||
|
||||
Let's look at the main program now. It derives `Bulldog` from `Dog` and provides
|
||||
an overrider for the new class:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$headers_namespaces/main.cpp[]
|
||||
----
|
||||
|
||||
Again ADL plays a role: it helps the overrider (and `main`) to locate the `poke`
|
||||
method.
|
||||
|
||||
This example is the "happy scenario", where namespaces are used conservatively.
|
||||
|
||||
The `OVERRIDE` macros don't interact well with `using` directives. For example
|
||||
this code:
|
||||
|
||||
```c++
|
||||
using namespace animals;
|
||||
using namespace canines;
|
||||
using namespace felines;
|
||||
|
||||
struct Bulldog : Dog {
|
||||
using Dog::Dog;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(Dog, Bulldog);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
poke, (std::ostream & os, virtual_ptr<Bulldog> dog), void) {
|
||||
next(os, dog);
|
||||
os << " and bites back";
|
||||
}
|
||||
```
|
||||
|
||||
...will fail to compile, with an error like "reference to
|
||||
'poke_boost_openmethod_overriders' is ambiguous". That is because the overrider
|
||||
containers exist in both the canines and felines namespaces, with the same name.
|
||||
|
||||
Finally, the names passed as first arguments to the BOOST_OPENMETHOD and
|
||||
BOOST_OPENMETHOD_OVERRIDE macros must be identifiers. Qualified names are not
|
||||
allowed. Consider:
|
||||
|
||||
```c++
|
||||
using animals::Animal;
|
||||
|
||||
namespace app_specific_behavior {
|
||||
|
||||
BOOST_OPENMETHOD(
|
||||
meet, (std::ostream&, virtual_ptr<Animal>, virtual_ptr<Animal>), void);
|
||||
|
||||
} // namespace app_specific_behavior
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
meet, (std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
|
||||
os << "ignore";
|
||||
}
|
||||
```
|
||||
|
||||
Here, the guide function cannot be found, even via ADL. We get an error like
|
||||
"use of undeclared identifier 'meet_boost_openmethod_guide'". How do we solve
|
||||
this? We might be tempted to use a qualified name:
|
||||
`app_specific_behavior::meet`:
|
||||
|
||||
```c++
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
app_specific_behavior::meet,
|
||||
(std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
|
||||
os << "ignore";
|
||||
}
|
||||
```
|
||||
|
||||
But `BOOST_OPENMETHOD_OVERRIDE` also uses the name to derive the overrider
|
||||
container's name, using preprocessor token pasting, resulting in an invalid
|
||||
declaration error.
|
||||
|
||||
We need to do is to make `BOOST_OPENMETHOD_OVERRIDE` "see" the guide function.
|
||||
Its name is returned by macro `BOOST_OPENMETHOD_GUIDE(NAME)`. We can use a
|
||||
using-declaration to bring the guide function into the current scope:
|
||||
|
||||
```c++
|
||||
using app_specific_behavior::BOOST_OPENMETHOD_GUIDE(meet);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
meet, (std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
|
||||
os << "ignore";
|
||||
}
|
||||
```
|
||||
@@ -64,7 +64,7 @@ Otherwise, there are serious problems with this approach.
|
||||
* Programs that use the Node classes get the overriders of
|
||||
`postfix`, even those that don't call `postfix` anywhere.
|
||||
|
||||
* They also pull in `postfix`'s transitive dependencies, in this case
|
||||
* They also pull in `postfix`{empty}'s transitive dependencies, in this case
|
||||
`std::ostream`, locales, facets, exceptions, etc. Note that now we need to
|
||||
include `<iostream>` _before_ defining the classes.
|
||||
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
:example: ../examples/rolex
|
||||
|
||||
## Multiple Dispatch
|
||||
|
||||
A method can have more than one `virtual_ptr` parameter. For example:
|
||||
A method can have more than one virtual parameter. This is often called
|
||||
"multi-methods" or "multiple dispatch". All the virtual parameters participate
|
||||
equally in overrider selection, following the same rules as those governing
|
||||
overload resolution - except that the selection happens at runtime, and takes
|
||||
into account the argument's dynamic types.
|
||||
|
||||
Multiple dispatch is occasionally useful, and, when it is needed, it can be
|
||||
difficult to implement correctly and efficiently by hand. For example, given the following classes:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$hello_world.cpp[tag=multi]
|
||||
include::{example}/7/main.cpp[tag=classes]
|
||||
----
|
||||
|
||||
We want to implement an `approve` method that determines who can make what kind
|
||||
of expenses. Employees can take any public transportation; managers can also
|
||||
take a taxi, for a ride cost up to $100.00; and founders can take any
|
||||
transportation, including a private jet. This can be expressed like so:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::example$hello_world.cpp[tag=multi_call,indent=0]
|
||||
include::{example}/7/main.cpp[tag=approve]
|
||||
----
|
||||
|
||||
The appropriate overrider is selected using a process similar to overload
|
||||
resolution, with fallback options. If one overrider is more specialized than all
|
||||
the others, call it. Otherwise, the return type is used as a tie-breaker, _if_
|
||||
it is covariant with the return type of the base method. If there is still no
|
||||
unique best overrider, one of the best overriders is chosen arbitrarily.
|
||||
Note that `approve` takes advantage of inheritance to avoid listing all possible
|
||||
cases explicitly. This is important, because the number of cases grows
|
||||
|
||||
49
doc/modules/ROOT/pages/namespaces.adoc
Normal file
49
doc/modules/ROOT/pages/namespaces.adoc
Normal file
@@ -0,0 +1,49 @@
|
||||
:example: ../examples/rolex
|
||||
|
||||
## Namespaces
|
||||
|
||||
Note;; This section uses overrider containers, described in the
|
||||
xref:headers.adoc[Headers] section.
|
||||
|
||||
xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] defines a method in the current
|
||||
namespace. xref:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE] works
|
||||
_across_ namespaces. Overriders are not required to be in the same namespace as
|
||||
the method they override. The macro adds the overrider a method that can be
|
||||
called with the same arguments, possibly located via argument dependant lookup.
|
||||
|
||||
Overrider containers are added to the current namespace. It follows that the
|
||||
same method can have overriders in several different container, in different
|
||||
namespaces. This must be taken into account when calling an overrider
|
||||
explicitly. Let's put Employee and Salesman in their own namespaces:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/4/roles.hpp[tag=content]
|
||||
----
|
||||
|
||||
When we try to compile `salesman.cpp`:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/3/salesman.cpp[tag=content]
|
||||
----
|
||||
|
||||
We get an error like:
|
||||
|
||||
```
|
||||
error: implicit instantiation of undefined template
|
||||
'sales::pay_boost_openmethod_overriders<
|
||||
double (boost::openmethod::virtual_ptr<const employees::Employee>)>
|
||||
```
|
||||
|
||||
This is because the overrider container for `pay` in namespace `sales` is
|
||||
specialized for `Salesman`, but not for `Employee`. The overrider for Employee
|
||||
is in a specialization of a different container, in namespace `employees`.
|
||||
|
||||
The solution is to qualify the overrider container with the `employees`
|
||||
namespace:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/3/salesman.cpp[tag=content]
|
||||
----
|
||||
@@ -5,7 +5,7 @@ Open-methods can be as fast as ordinary virtual member functions when
|
||||
compiled with optimization.
|
||||
|
||||
First, let's examine the code generated by clang for an ordinary virtual
|
||||
function call.
|
||||
function call:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
@@ -38,19 +38,19 @@ This compiles to (variable names are shortened for readability):
|
||||
|
||||
[source,asm]
|
||||
----
|
||||
mov rax, rdi
|
||||
mov rcx, qword ptr [rdi]
|
||||
mov rdi, qword ptr [rip + mult]
|
||||
imul rdi, qword ptr [rcx - 8]
|
||||
movzx ecx, byte ptr [rip + shift]
|
||||
shr rdi, cl
|
||||
mov rdx, rsi
|
||||
mov rcx, qword ptr [rip + vptr_vector_vptrs]
|
||||
mov rdi, qword ptr [rcx + 8*rdi]
|
||||
mov rcx, qword ptr [rip + fn+88]
|
||||
mov rcx, qword ptr [rdi + 8*rcx]
|
||||
mov rsi, rax
|
||||
jmp rcx # TAILCALL
|
||||
mov rax, rdi
|
||||
mov rcx, qword ptr [rdi]
|
||||
mov rdi, qword ptr [rip + mult]
|
||||
imul rdi, qword ptr [rcx - 8]
|
||||
movzx ecx, byte ptr [rip + shift]
|
||||
shr rdi, cl
|
||||
mov rdx, rsi
|
||||
mov rcx, qword ptr [rip + vptr_vector_vptrs]
|
||||
mov rdi, qword ptr [rcx + 8*rdi]
|
||||
mov rcx, qword ptr [rip + fn+88]
|
||||
mov rcx, qword ptr [rdi + 8*rcx]
|
||||
mov rsi, rax
|
||||
jmp rcx # TAILCALL
|
||||
----
|
||||
|
||||
This is quite a few instructions more. Upon closer examination, we see that many
|
||||
@@ -62,9 +62,9 @@ llvm-mca estimates a throughput of 4 cycles per dispatch. However, the
|
||||
difference is amortized by the time spent passing the arguments and returning
|
||||
from the function; plus, of course, executing the body of the function.
|
||||
|
||||
Micro and RDTSC-based benchmarks suggest that dispatching an open-methods with a
|
||||
single virtual argument _via_ _a_ _reference_ is between 30% and 50% slower than
|
||||
calling the equivalent virtual function, with an empty body and no other
|
||||
Micro- and RDTSC-based benchmarks suggest that dispatching an open-methods with
|
||||
a single virtual argument _via_ _a_ _reference_ is between 30% and 50% slower
|
||||
than calling the equivalent virtual function, with an empty body and no other
|
||||
arguments. In most real programs, the overhead would be unnoticeable.
|
||||
|
||||
*However*, `call_via_ref` does two things: it constructs a `virtual_ptr<Node>`
|
||||
@@ -94,10 +94,10 @@ mov rax, qword ptr [rdi + 8*rax]
|
||||
jmp rax # TAILCALL
|
||||
----
|
||||
|
||||
`virtual_ptr` arguments as passed through the method call, to the overrider,
|
||||
`virtual_ptr` arguments are passed through the method call, to the overrider,
|
||||
which can use them to make further method calls.
|
||||
|
||||
A program designed with open-methods in mind should store `virtual_ptr`{empty}'s
|
||||
A program designed with open-methods in mind should use `virtual_ptr`{empty}'s
|
||||
in place of plain pointers or references, as much as possible. Here is the Node
|
||||
example, rewritten to use `virtual_ptr`{empty}'s thoughout:
|
||||
|
||||
|
||||
111
doc/modules/ROOT/pages/ref_headers.adoc
Normal file
111
doc/modules/ROOT/pages/ref_headers.adoc
Normal file
@@ -0,0 +1,111 @@
|
||||
[#ref_headers]
|
||||
= xref:ref_headers.adoc[Headers]
|
||||
|
||||
{empty}
|
||||
|
||||
## Headers for General Use
|
||||
|
||||
The following headers are sufficient for most basic uses of the library.
|
||||
|
||||
* xref:#main[`boost/openmethod.hpp`] to define open-methods and overriders using
|
||||
convenient macros.
|
||||
|
||||
* xref:#initialize[`boost/openmethod/initialize.hpp`] to initialize the library.
|
||||
Typically only included in the translation unit containing `main`.
|
||||
|
||||
The following headers make it possible to use standard smart pointers in virtual
|
||||
parameters:
|
||||
|
||||
* xref:#std_shared_ptr[`boost/openmethod/interop/std_shared_ptr.hpp`] to use
|
||||
`std::shared_ptr` in virtual parameters.
|
||||
|
||||
* xref:#std_unique_ptr[`boost/openmethod/interop/std_unique_ptr.hpp`] to use
|
||||
`std::unique_ptr` in virtual parameters.
|
||||
|
||||
*The remaining headers are for advanced use*.
|
||||
|
||||
## Pre-Core Headers
|
||||
|
||||
The following headers can be included before `core.hpp` to define custom
|
||||
registries and policies, and override the default registry by defining
|
||||
xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[`BOOST_OPENMETHOD_DEFAULT_REGISTRY`].
|
||||
|
||||
### boost/openmethod/preamble.hpp
|
||||
|
||||
Defines `registry` and stock policy categories. Also defines all types and
|
||||
functions necessary for the definition of `registry`.
|
||||
|
||||
### boost/openmethod/policies/std_rtti.hpp
|
||||
|
||||
Provides an implementation of the `rtti` policy using standard RTTI.
|
||||
|
||||
### boost/openmethod/policies/fast_perfect_hash.hpp
|
||||
|
||||
Provides an implementation of the `hash` policy using a fast perfect hash
|
||||
function.
|
||||
|
||||
### boost/openmethod/policies/vptr_vector.hpp
|
||||
|
||||
Provides an implementation of the `vptr` policy that stores the v-table pointers
|
||||
in a `std::vector` indexed by type ids, possibly hashed.
|
||||
|
||||
### boost/openmethod/policies/default_error_handler.hpp
|
||||
|
||||
Provides an implementation of the `error_handler` policy that calls a
|
||||
`std::function<void(openmethod_error)>` when an error is encountered, and before
|
||||
the library aborts the program.
|
||||
|
||||
### boost/openmethod/policies/stderr_output.hpp
|
||||
|
||||
Provides an implementation of the `output` policy that writes diagnostics to
|
||||
the C standard error stream (not using iostreams).
|
||||
|
||||
### boost/openmethod/default_registry.hpp
|
||||
|
||||
Defines the default registry, which contains all the stock policies listed
|
||||
above. Includes all the headers listed in the preamble section so far.
|
||||
|
||||
### boost/openmethod/policies/static_rtti.hpp
|
||||
|
||||
Provides a minimal implementation of the `rtti` policy that does not depend on
|
||||
standard RTTI.
|
||||
|
||||
### boost/openmethod/policies/throw_error_handler.hpp
|
||||
|
||||
Provides an implementation of the `error_handler` policy that throws errors as
|
||||
exceptions.
|
||||
|
||||
### boost/openmethod/policies/vptr_map.hpp
|
||||
|
||||
Provides an implementation of the `vptr` policy that stores the v-table pointers
|
||||
in a map (by default a `std::map`) indexed by type ids.
|
||||
|
||||
## High-level Headers
|
||||
|
||||
### boost/openmethod/core.hpp
|
||||
|
||||
Defines the main constructs of the library: methods, overriders and virtual
|
||||
pointers, and mechanisms to implement them. Does not define any public macros
|
||||
apart from `BOOST_OPENMETHOD_DEFAULT_REGISTRY`, if it is not defined already.
|
||||
|
||||
### boost/openmethod/macros.hpp
|
||||
|
||||
Defines the public macros of the library, such as `BOOST_OPENMETHOD`,
|
||||
`BOOST_OPENMETHOD_CLASSES`, etc.
|
||||
|
||||
There is little point in including this header directly, as this has the same
|
||||
effect as including `boost/openmethod.hpp`, which is shorter.
|
||||
|
||||
### boost/openmethod.hpp
|
||||
|
||||
Includes `core.hpp` and `macros.hpp`.
|
||||
|
||||
### boost/openmethod/interop/std_shared_ptr.hpp
|
||||
|
||||
Provides a `virtual_traits` specialization that make it possible to use a
|
||||
`std::shared_ptr` in place of a raw pointer or reference in virtual parameters.
|
||||
|
||||
### boost/openmethod/interop/std_unique_ptr.hpp
|
||||
|
||||
Provides a `virtual_traits` specialization that make it possible to use a
|
||||
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.
|
||||
@@ -22,15 +22,35 @@ counterparts - e.g. from `virtual_ptr<std::unique_ptr<const Node>>` to
|
||||
`virtual_ptr<const Node>`. Methods and overriders typically use plain
|
||||
`virtual_ptr`{empty}'s, although it is not always the case. For example,
|
||||
consider a `transpose` method for matrices. If the matrix is symmetric, the
|
||||
overrider should return its argument. This can be implemented by passing a `virtual_ptr<std::shared_ptr<const Matrix>>` to the method.
|
||||
overrider should return its argument. This can be implemented by passing a
|
||||
`virtual_ptr<std::shared_ptr<const Matrix>>` to the method.
|
||||
|
||||
The reverse conversion, from plain to smart, does not exist, because it would be
|
||||
The reverse conversion, from plain to smart, does not exist, because it is
|
||||
unsafe.
|
||||
|
||||
A smart `virtual_ptr` can be constructed from a corresponding smart pointer, but
|
||||
not directly from a plain reference or pointer, because it has the potential to
|
||||
accidentally create smart pointers.
|
||||
|
||||
The library provides aliases for standard smart pointers:
|
||||
|
||||
- `unique_virtual_ptr<Class>` is an alias for `virtual_ptr<std::unique_ptr<Class>>`
|
||||
|
||||
- `shared_virtual_ptr<Class>` is an alias for `virtual_ptr<std::shared_ptr<Class>>`
|
||||
|
||||
The standard library provides `std::make_unique` and `std::make_shared` to
|
||||
create smart pointers. They are convenient, robust, and, in the case of
|
||||
`std::shared_ptr`, more efficient. OpenMethod provides counterparts:
|
||||
|
||||
- `make_unique_virtual_ptr<Class>(...)`
|
||||
|
||||
- `make_shared_virtual_ptr<Class>(...)`
|
||||
|
||||
Since these functions create the object, they know its exact type with
|
||||
certainty. Thus they don't need to perform a hash table lookup to find the
|
||||
appropriate v-table; they simply read it from a static variable. As a
|
||||
consequence, they don't even require `Class` to be polymorphic.
|
||||
|
||||
Here is a variation of the AST example that uses dynamic allocation and unique
|
||||
pointers:
|
||||
|
||||
|
||||
@@ -1813,7 +1813,8 @@ auto operator!=(
|
||||
template<class Class, class Registry>
|
||||
struct virtual_traits<virtual_ptr<Class, Registry>, Registry> {
|
||||
//! `Class`, stripped from cv-qualifiers.
|
||||
using virtual_type = typename virtual_ptr<Class, Registry>::element_type;
|
||||
using virtual_type =
|
||||
std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;
|
||||
|
||||
//! Return a reference to a non-modifiable `Class` object.
|
||||
//! @param arg A reference to a non-modifiable `Class` object.
|
||||
@@ -1858,7 +1859,8 @@ struct virtual_traits<virtual_ptr<Class, Registry>, Registry> {
|
||||
template<class Class, class Registry>
|
||||
struct virtual_traits<const virtual_ptr<Class, Registry>&, Registry> {
|
||||
//! `Class`, stripped from cv-qualifiers.
|
||||
using virtual_type = typename virtual_ptr<Class, Registry>::element_type;
|
||||
using virtual_type =
|
||||
std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;
|
||||
|
||||
//! Return a reference to a non-modifiable `Class` object.
|
||||
//! @param arg A reference to a non-modifiable `Class` object.
|
||||
@@ -2317,8 +2319,7 @@ class method<Id, ReturnType(Parameters...), Registry>
|
||||
(void)&impl;
|
||||
}
|
||||
|
||||
static override_impl<Function, FnReturnType>
|
||||
impl;
|
||||
static override_impl<Function, FnReturnType> impl;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2628,21 +2629,45 @@ template<class T, class R>
|
||||
struct validate_overrider_parameter<virtual_ptr<T, R>, virtual_ptr<T, R>, void>
|
||||
: std::true_type {};
|
||||
|
||||
template<class T1, class R1, class T2, class R2>
|
||||
template<class T1, class R, class T2, class R2>
|
||||
struct validate_overrider_parameter<
|
||||
virtual_ptr<T1, R1>, virtual_ptr<T2, R2>, void> : std::is_same<R1, R2> {
|
||||
static_assert(validate_overrider_parameter::value, "registry mismatch");
|
||||
virtual_ptr<T1, R>, virtual_ptr<T2, R2>, void> : std::true_type {
|
||||
static_assert(std::is_same_v<R, R2>, "registry mismatch");
|
||||
using C1 = virtual_type<virtual_ptr<T1, R>, R>;
|
||||
using C2 = virtual_type<virtual_ptr<T2, R>, R>;
|
||||
static_assert(
|
||||
std::is_base_of_v<C1, C2> &&
|
||||
std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
|
||||
"method parameter must be an unambiguous accessible base "
|
||||
"of corresponding overrider parameter");
|
||||
};
|
||||
|
||||
template<class T1, class R1, class T2, class R2>
|
||||
template<class T1, class R, class T2, class R2>
|
||||
struct validate_overrider_parameter<
|
||||
const virtual_ptr<T1, R1>&, const virtual_ptr<T2, R2>&, void>
|
||||
: validate_overrider_parameter<virtual_ptr<T1, R1>, virtual_ptr<T2, R2>> {};
|
||||
const virtual_ptr<T1, R>&, const virtual_ptr<T2, R2>&, void>
|
||||
: std::true_type {
|
||||
static_assert(std::is_same_v<R, R2>, "registry mismatch");
|
||||
using C1 = virtual_type<const virtual_ptr<T1, R>&, R>;
|
||||
using C2 = virtual_type<const virtual_ptr<T2, R>&, R>;
|
||||
static_assert(
|
||||
std::is_base_of_v<C1, C2> &&
|
||||
std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
|
||||
"method parameter must be an unambiguous accessible base "
|
||||
"of corresponding overrider parameter");
|
||||
};
|
||||
|
||||
template<class T1, class R1, class T2, class R2>
|
||||
template<class T1, class R, class T2, class R2>
|
||||
struct validate_overrider_parameter<
|
||||
virtual_ptr<T1, R1>&&, virtual_ptr<T2, R2>&&, void>
|
||||
: validate_overrider_parameter<virtual_ptr<T1, R1>, virtual_ptr<T2, R2>> {};
|
||||
virtual_ptr<T1, R>&&, virtual_ptr<T2, R2>&&, void> : std::true_type {
|
||||
static_assert(std::is_same_v<R, R2>, "registry mismatch");
|
||||
using C1 = virtual_type<virtual_ptr<T1, R>&&, R>;
|
||||
using C2 = virtual_type<virtual_ptr<T2, R>&&, R>;
|
||||
static_assert(
|
||||
std::is_base_of_v<C1, C2> &&
|
||||
std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
|
||||
"method parameter must be an unambiguous accessible base "
|
||||
"of corresponding overrider parameter");
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ struct va_args<ReturnType> {
|
||||
::boost::openmethod::detail::va_args<__VA_ARGS__>::registry>
|
||||
|
||||
#define BOOST_OPENMETHOD(NAME, ARGS, ...) \
|
||||
template<typename...> \
|
||||
struct BOOST_OPENMETHOD_OVERRIDERS(NAME); \
|
||||
struct BOOST_OPENMETHOD_ID(NAME); \
|
||||
template<typename... ForwarderParameters> \
|
||||
typename ::boost::openmethod::detail::enable_forwarder< \
|
||||
@@ -70,8 +72,8 @@ struct va_args<ReturnType> {
|
||||
ForwarderParameters...>::type \
|
||||
BOOST_OPENMETHOD_GUIDE(NAME)(ForwarderParameters && ... args); \
|
||||
template<typename... ForwarderParameters> \
|
||||
inline auto NAME(ForwarderParameters&&... args) \
|
||||
->typename ::boost::openmethod::detail::enable_forwarder< \
|
||||
inline auto NAME(ForwarderParameters&&... args) -> \
|
||||
typename ::boost::openmethod::detail::enable_forwarder< \
|
||||
void, BOOST_OPENMETHOD_TYPE(NAME, ARGS, __VA_ARGS__), \
|
||||
::boost::openmethod::detail::va_args<__VA_ARGS__>::return_type, \
|
||||
ForwarderParameters...>::type { \
|
||||
@@ -89,27 +91,24 @@ struct va_args<ReturnType> {
|
||||
};
|
||||
|
||||
#define BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, ...) \
|
||||
template<typename> \
|
||||
template<typename...> \
|
||||
struct BOOST_OPENMETHOD_OVERRIDERS(NAME); \
|
||||
template<> \
|
||||
struct BOOST_OPENMETHOD_OVERRIDERS(NAME)<__VA_ARGS__ ARGS> { \
|
||||
BOOST_OPENMETHOD_DETAIL_LOCATE_METHOD(NAME, ARGS); \
|
||||
static auto fn ARGS -> __VA_ARGS__; \
|
||||
static auto fn ARGS->__VA_ARGS__; \
|
||||
static auto has_next() -> bool; \
|
||||
template<typename... Args> \
|
||||
static auto next(Args&&... args) -> decltype(auto); \
|
||||
}; \
|
||||
inline auto BOOST_OPENMETHOD_OVERRIDERS( \
|
||||
NAME)<__VA_ARGS__ ARGS>::has_next() \
|
||||
->bool { \
|
||||
NAME)<__VA_ARGS__ ARGS>::has_next() -> bool { \
|
||||
return boost_openmethod_detail_locate_method_aux< \
|
||||
void ARGS>::type::has_next<fn>(); \
|
||||
} \
|
||||
template<typename... Args> \
|
||||
inline auto BOOST_OPENMETHOD_OVERRIDERS(NAME)<__VA_ARGS__ ARGS>::next( \
|
||||
Args&&... args) \
|
||||
->decltype(auto) { \
|
||||
BOOST_ASSERT(has_next()); \
|
||||
Args&&... args) -> decltype(auto) { \
|
||||
return boost_openmethod_detail_locate_method_aux< \
|
||||
void ARGS>::type::next<fn>(std::forward<Args>(args)...); \
|
||||
}
|
||||
@@ -124,7 +123,7 @@ struct va_args<ReturnType> {
|
||||
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(NAME, ARGS, ...) \
|
||||
BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
|
||||
auto BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, __VA_ARGS__)::fn ARGS \
|
||||
->boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
|
||||
-> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
|
||||
|
||||
#define BOOST_OPENMETHOD_OVERRIDE(NAME, ARGS, ...) \
|
||||
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
|
||||
@@ -134,7 +133,7 @@ struct va_args<ReturnType> {
|
||||
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
|
||||
BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
|
||||
inline auto BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, __VA_ARGS__)::fn ARGS \
|
||||
->boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
|
||||
-> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
|
||||
|
||||
#define BOOST_OPENMETHOD_CLASSES(...) \
|
||||
BOOST_OPENMETHOD_REGISTER(::boost::openmethod::use_classes<__VA_ARGS__>);
|
||||
|
||||
Reference in New Issue
Block a user