Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ BraceWrapping:
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakInheritanceList: AfterColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializers: AfterColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
Expand Down Expand Up @@ -102,7 +102,7 @@ PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: false
SortIncludes: false
Expand Down Expand Up @@ -133,4 +133,6 @@ StatementMacros:
TabWidth: 8
UseCRLF: false
UseTab: Never
TemplateNames:
- register_classes
...
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,48 @@ jobs:
COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}

reflection:
name: C++26 reflection
runs-on: ubuntu-24.04
steps:
- name: Install GCC 16
run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install -y g++-16 cmake ninja-build

- name: Clone Boost.OpenMethod
uses: actions/checkout@v4

- name: Clone Boost
uses: alandefreitas/cpp-actions/boost-clone@v1.8.8
with:
# boost-clone excludes `test` and `tests` from the scan
# (modules-exclude-paths defaults to them), so it never sees the
# includes in our own test/ and would clone neither Boost.Test nor
# Boost.DLL - CMake then fails to generate on a missing
# Boost::unit_test_framework. `modules` is unioned with the scan,
# and their own dependencies are resolved afterwards.
modules: test dll
branch: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }}
boost-dir: ../boost-source
scan-modules-dir: .
scan-modules-ignore: openmethod

# The suite is the reflection test: BOOST_OPENMETHOD_TEST_CLASSES expands
# to nothing here, so every class has to be found by use_classes_in.
- name: Build and test
run: |
cmake -S . -B ../build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_COMPILER=g++-16 \
-DBOOST_OPENMETHOD_ENABLE_REFLECTION=ON \
-DBOOST_OPENMETHOD_BUILD_TESTS=ON \
-DBOOST_OPENMETHOD_WARNINGS_AS_ERRORS=ON \
-DBOOST_SRC_DIR="$(cd .. && pwd)/boost-source"
cmake --build ../build --target tests -j $(nproc)
ctest --test-dir ../build -j $(nproc) --output-on-failure

antora:
name: Antora docs
strategy:
Expand Down
75 changes: 75 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,81 @@ option(
BOOST_OPENMETHOD_WARNINGS_AS_ERRORS
"Treat warnings as errors"
OFF)
option(
BOOST_OPENMETHOD_ENABLE_REFLECTION
"Build the tests and examples with C++26 reflection enabled"
OFF)

# C++26 reflection (P2996). The library detects it on its own, from
# __cpp_impl_reflection; this only arranges for the tests to be built in a mode
# where the compiler provides it, which needs both C++26 and, on GCC, an opt-in
# flag. It is applied per target rather than through CMAKE_CXX_FLAGS, because
# CMake probes the compiler before CMAKE_CXX_STANDARD takes effect and GCC
# rejects -freflection under any other standard.
set(BOOST_OPENMETHOD_REFLECTION_OPTIONS "")

if (BOOST_OPENMETHOD_ENABLE_REFLECTION)
include(CheckCXXSourceCompiles)

set(BOOST_OPENMETHOD_REFLECTION_TEST_SOURCE [[
#include <meta>
struct Base {};
struct Derived : Base {};
consteval auto count() -> int {
return static_cast<int>(
std::meta::bases_of(
^^Derived, std::meta::access_context::unchecked()).size());
}
static_assert(count() == 1);
int main() {}
]])

set(CMAKE_REQUIRED_QUIET ON)

foreach(candidate "-std=c++26" "-std=c++26;-freflection")
string(REPLACE ";" " " candidate_flags "${candidate}")
set(CMAKE_REQUIRED_FLAGS "${candidate_flags}")
unset(BOOST_OPENMETHOD_HAS_REFLECTION CACHE)
check_cxx_source_compiles(
"${BOOST_OPENMETHOD_REFLECTION_TEST_SOURCE}"
BOOST_OPENMETHOD_HAS_REFLECTION)

if (BOOST_OPENMETHOD_HAS_REFLECTION)
set(BOOST_OPENMETHOD_REFLECTION_OPTIONS ${candidate})
break()
endif()
endforeach()

unset(CMAKE_REQUIRED_FLAGS)
unset(CMAKE_REQUIRED_QUIET)

if (NOT BOOST_OPENMETHOD_HAS_REFLECTION)
message(
FATAL_ERROR
"BOOST_OPENMETHOD_ENABLE_REFLECTION is ON but ${CMAKE_CXX_COMPILER_ID} "
"${CMAKE_CXX_COMPILER_VERSION} does not support C++26 reflection")
endif()

message(
STATUS
"Boost.OpenMethod: C++26 reflection enabled"
" [${BOOST_OPENMETHOD_REFLECTION_OPTIONS}]")
endif()

# Build `target` with C++26 reflection, if BOOST_OPENMETHOD_ENABLE_REFLECTION is
# ON. Does nothing otherwise, so callers need no condition of their own.
function(boost_openmethod_enable_reflection target)
if (NOT BOOST_OPENMETHOD_ENABLE_REFLECTION)
return()
endif()

# The standard flag is passed here rather than through CXX_STANDARD: CMake
# learned the value 26 only in 3.30, and this project supports older ones.
# target_compile_options come after the flag CMake derives from the
# library's cxx_std_17 requirement, and the last -std wins.
target_compile_options(
${target} PRIVATE ${BOOST_OPENMETHOD_REFLECTION_OPTIONS})
endfunction()

if (BOOST_OPENMETHOD_BUILD_EXAMPLES AND NOT BOOST_OPENMETHOD_BUILD_TESTS)
message(
Expand Down
2 changes: 2 additions & 0 deletions doc/modules/ROOT/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ foreach (cpp ${cpp_files})
get_filename_component(stem ${cpp} NAME_WE)
set(test_target "boost_openmethod-${stem}")
add_executable(${test_target} ${cpp})
boost_openmethod_enable_reflection(${test_target})
target_link_libraries(${test_target} PRIVATE Boost::openmethod Boost::unit_test_framework)
add_test(NAME ${test_target} COMMAND ${test_target})
add_dependencies(tests ${test_target})
Expand All @@ -43,6 +44,7 @@ function(boost_openmethod_add_step_by_step dir)
file(GLOB cpp_files "${subdir}/*.cpp")
set(target "boost_openmethod-${dir}_${subex}")
add_executable(${target} ${cpp_files})
boost_openmethod_enable_reflection(${target})
target_link_libraries(${target} PRIVATE Boost::openmethod)
set(output_dir openmethod/${dir}/${subex})
set_target_properties(${target} PROPERTIES
Expand Down
14 changes: 8 additions & 6 deletions doc/modules/ROOT/examples/accept_no_visitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ struct Node {
struct Plus : Node {
Plus(
shared_virtual_ptr<const Node> left,
shared_virtual_ptr<const Node> right)
: left(std::move(left)), right(std::move(right)) {
shared_virtual_ptr<const Node> right) :
left(std::move(left)), right(std::move(right)) {
}

shared_virtual_ptr<const Node> left, right;
Expand All @@ -34,8 +34,8 @@ struct Plus : Node {
struct Times : Node {
Times(
shared_virtual_ptr<const Node> left,
shared_virtual_ptr<const Node> right)
: left(std::move(left)), right(std::move(right)) {
shared_virtual_ptr<const Node> right) :
left(std::move(left)), right(std::move(right)) {
}

shared_virtual_ptr<const Node> left, right;
Expand Down Expand Up @@ -82,7 +82,8 @@ BOOST_OPENMETHOD_OVERRIDE(as_forth, (virtual_ptr<const Times> node), string) {
return as_forth(node->left) + " " + as_forth(node->right) + " *";
}

BOOST_OPENMETHOD_OVERRIDE(as_forth, (virtual_ptr<const Variable> node), string) {
BOOST_OPENMETHOD_OVERRIDE(
as_forth, (virtual_ptr<const Variable> node), string) {
return std::to_string(node->value);
}

Expand Down Expand Up @@ -111,7 +112,8 @@ auto main() -> int {
shared_virtual_ptr<Node> node = make_shared_virtual<Times>(
make_shared_virtual<Variable>(2),
make_shared_virtual<Plus>(
make_shared_virtual<Variable>(3), make_shared_virtual<Variable>(4)));
make_shared_virtual<Variable>(3),
make_shared_virtual<Variable>(4)));

cout << as_forth(node) << " = " << as_lisp(node) << " = " << value(node)
<< "\n";
Expand Down
3 changes: 1 addition & 2 deletions doc/modules/ROOT/examples/inplace_vptr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ struct Cat : Animal, inplace_vptr_derived<Cat, Animal> {};

struct Dog : Animal, inplace_vptr_derived<Dog, Animal> {};

BOOST_OPENMETHOD(
poke, (virtual_<Animal&> animal, std::ostream& os), void);
BOOST_OPENMETHOD(poke, (virtual_<Animal&> animal, std::ostream& os), void);

BOOST_OPENMETHOD_OVERRIDE(poke, (Cat&, std::ostream& os), void) {
os << "hiss\n";
Expand Down
4 changes: 2 additions & 2 deletions doc/modules/ROOT/examples/matrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ struct abstract {
int ref_count = 0;
};

struct registry
: boost::openmethod::registry<boost::openmethod::policies::static_rtti> {};
struct registry :
boost::openmethod::registry<boost::openmethod::policies::static_rtti> {};

template<class Rep>
using matrix_ptr = boost::openmethod::virtual_ptr<Rep, registry>;
Expand Down
9 changes: 6 additions & 3 deletions doc/modules/ROOT/examples/matrix_readme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ BOOST_OPENMETHOD_CLASSES(Matrix, SquareMatrix, SymmetricMatrix, DiagonalMatrix);

BOOST_OPENMETHOD(to_json, (virtual_ptr<const Matrix>, std::ostream& os), void);

BOOST_OPENMETHOD_OVERRIDE(to_json, (virtual_ptr<const SquareMatrix>, std::ostream& os), void) {
BOOST_OPENMETHOD_OVERRIDE(
to_json, (virtual_ptr<const SquareMatrix>, std::ostream& os), void) {
os << "all the elements\n";
}

BOOST_OPENMETHOD_OVERRIDE(to_json, (virtual_ptr<const SymmetricMatrix>, std::ostream& os), void) {
BOOST_OPENMETHOD_OVERRIDE(
to_json, (virtual_ptr<const SymmetricMatrix>, std::ostream& os), void) {
os << "elements above and including the diagonal\n";
}

BOOST_OPENMETHOD_OVERRIDE(to_json, (virtual_ptr<const DiagonalMatrix>, std::ostream& os), void) {
BOOST_OPENMETHOD_OVERRIDE(
to_json, (virtual_ptr<const DiagonalMatrix>, std::ostream& os), void) {
os << "just the diagonal\n";
}

Expand Down
5 changes: 3 additions & 2 deletions doc/modules/ROOT/examples/rolex/1/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ int main() {
boost::openmethod::initialize();

Employee bill;
Salesman bob; bob.sales = 100'000.0;
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
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]
4 changes: 3 additions & 1 deletion doc/modules/ROOT/examples/rolex/1/roles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#include <boost/openmethod.hpp>

struct Employee { virtual ~Employee() = default; };
struct Employee {
virtual ~Employee() = default;
};

struct Salesman : Employee {
double sales = 0.0;
Expand Down
5 changes: 3 additions & 2 deletions doc/modules/ROOT/examples/rolex/2/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ int main() {
boost::openmethod::initialize();

Employee bill;
Salesman bob; bob.sales = 100'000.0;
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
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]
4 changes: 3 additions & 1 deletion doc/modules/ROOT/examples/rolex/2/roles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#include <boost/openmethod.hpp>

struct Employee { virtual ~Employee() = default; };
struct Employee {
virtual ~Employee() = default;
};

struct Salesman : Employee {
double sales = 0.0;
Expand Down
5 changes: 3 additions & 2 deletions doc/modules/ROOT/examples/rolex/3/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ int main() {
boost::openmethod::initialize();

Employee bill;
Salesman bob; bob.sales = 100'000.0;
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
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]
4 changes: 3 additions & 1 deletion doc/modules/ROOT/examples/rolex/3/roles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#include <boost/openmethod.hpp>

struct Employee { virtual ~Employee() = default; };
struct Employee {
virtual ~Employee() = default;
};

struct Salesman : Employee {
double sales = 0.0;
Expand Down
5 changes: 3 additions & 2 deletions doc/modules/ROOT/examples/rolex/4/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ int main() {
boost::openmethod::initialize();

employees::Employee bill;
sales::Salesman bob; bob.sales = 100'000.0;
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
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]
2 changes: 1 addition & 1 deletion doc/modules/ROOT/examples/rolex/4/roles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ BOOST_OPENMETHOD_INLINE_OVERRIDE(
return 5000.0;
}

}
} // namespace employees

namespace sales {

Expand Down
3 changes: 2 additions & 1 deletion doc/modules/ROOT/examples/rolex/4/salesman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ 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),
pay,
(boost::openmethod::virtual_ptr<const employees::Employee> emp),
double)::fn(emp) +
emp->sales * 0.05; // base + commission
}
Expand Down
7 changes: 4 additions & 3 deletions doc/modules/ROOT/examples/rolex/5/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class Payroll {
}

friend BOOST_OPENMETHOD_OVERRIDER(
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
pay,
(Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double);
friend BOOST_OPENMETHOD_OVERRIDER(
pay,
Expand Down Expand Up @@ -83,8 +84,8 @@ int main() {
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 << "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[]
4 changes: 2 additions & 2 deletions doc/modules/ROOT/examples/throw_error_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ struct throw_if_not_implemented : bom::policies::error_handler {
};
};

struct custom_registry : bom::default_registry::with<throw_if_not_implemented> {
};
struct custom_registry :
bom::default_registry::with<throw_if_not_implemented> {};

using boost::openmethod::virtual_ptr;

Expand Down
Loading