static checks for invalid method params

This commit is contained in:
Jean-Louis Leroy
2025-06-30 17:06:06 -04:00
parent 489f1d27d8
commit 6a9ccf170b
6 changed files with 149 additions and 65 deletions

View File

@@ -301,7 +301,7 @@ constexpr bool is_virtual_ptr = detail::is_virtual_ptr_aux<T>::value;
void boost_openmethod_vptr(...);
template<class Registry, class Class>
template<class Class, class Registry>
constexpr bool has_vptr_fn = std::is_same_v<
decltype(boost_openmethod_vptr(
std::declval<const Class&>(), std::declval<Registry*>())),
@@ -311,7 +311,7 @@ template<class Registry, class ArgType>
decltype(auto) acquire_vptr(const ArgType& arg) {
Registry::check_initialized();
if constexpr (detail::has_vptr_fn<Registry, ArgType>) {
if constexpr (detail::has_vptr_fn<ArgType, Registry>) {
return boost_openmethod_vptr(arg, static_cast<Registry*>(nullptr));
} else {
return Registry::template policy<policies::vptr>::dynamic_vptr(arg);
@@ -974,6 +974,16 @@ template<class Registry, typename Type, class OtherRegistry>
struct using_same_registry<Registry, const virtual_ptr<Type, OtherRegistry>&>
: std::is_same<Registry, OtherRegistry> {};
template<typename, class>
struct valid_method_parameter : std::true_type {};
template<typename T, class Registry>
struct valid_method_parameter<virtual_<T>, Registry>
: std::bool_constant<
has_vptr_fn<virtual_type<T, Registry>, Registry> ||
Registry::rtti::template is_polymorphic<
virtual_type<T, Registry>>> {};
} // namespace detail
template<
@@ -1003,10 +1013,15 @@ class method<Name, auto(Parameters...)->ReturnType, Registry>
mp11::mp_list<Parameters...>, detail::is_virtual>::value;
// sanity checks
static_assert(Arity > 0, "method must have at least one virtual argument");
static_assert(Arity > 0, "method has no virtual parameters");
static_assert(
(true && ... &&
detail::using_same_registry<Registry, Parameters>::value));
(... && detail::using_same_registry<Registry, Parameters>::value),
"method and parameters use different registries");
static_assert(
(... && detail::valid_method_parameter<Parameters, Registry>::value),
"virtual_<> parameter is not polymorphic and no boost_openmethod_vptr "
"function is available");
//static_assert()
type_id vp_type_ids[Arity];

View File

@@ -0,0 +1,12 @@
// 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 <boost/openmethod.hpp>
using boost::openmethod::virtual_;
struct Cat {};
BOOST_OPENMETHOD(poke, (virtual_<Cat>), void);

View File

@@ -0,0 +1,12 @@
// 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 <boost/openmethod.hpp>
using namespace boost::openmethod;
struct Cat {};
BOOST_OPENMETHOD(poke, (virtual_ptr<Cat, release_registry>), void, debug_registry);

View File

@@ -231,7 +231,9 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a_b1_c) {
B1 C
*/
struct A {};
struct A {
virtual ~A() = default;
};
struct B : A {};
struct C : A {};
@@ -258,7 +260,9 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_c1) {
B1 C1
*/
struct A {};
struct A {
virtual ~A() = default;
};
struct B : A {};
struct C : A {};
@@ -294,7 +298,9 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1) {
D1
*/
struct A {};
struct A {
virtual ~A() = default;
};
struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {};
@@ -338,7 +344,9 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1_e2) {
D1 E2 but E can use them
*/
struct A {};
struct A {
virtual ~A() = default;
};
struct B : virtual A {};
struct C : virtual A {};
struct E : C {};
@@ -396,8 +404,12 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_c1_b1) {
C1
*/
struct A {};
struct B {};
struct A {
virtual ~A() = default;
};
struct B {
virtual ~B() = default;
};
struct C : A, B {};
BOOST_OPENMETHOD_REGISTER(use_classes<A, test_registry>);

View File

@@ -16,6 +16,7 @@ using namespace boost::openmethod::detail;
namespace mp11 = boost::mp11;
#include <boost/openmethod.hpp>
#include <boost/openmethod/with_vptr.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include "test_util.hpp"
@@ -138,6 +139,28 @@ static_assert(detail::using_same_registry<registry1, int>::value);
static_assert(
!detail::using_same_registry<registry1, virtual_ptr<a, registry2>>::value);
static_assert(valid_method_parameter<default_registry, virtual_<a&>>::value);
static_assert(
valid_method_parameter<default_registry, virtual_<const a&>>::value);
static_assert(valid_method_parameter<default_registry, int>::value);
struct non_polymorphic {};
static_assert(!valid_method_parameter<
virtual_<non_polymorphic&>, default_registry>::value);
struct non_polymorphic_with_vptr {};
auto boost_openmethod_vptr(const non_polymorphic_with_vptr&, void*)
-> vptr_type;
static_assert(valid_method_parameter<
virtual_<non_polymorphic_with_vptr&>, default_registry>::value);
static_assert(
valid_method_parameter<
virtual_<const non_polymorphic_with_vptr&>, default_registry>::value);
// clang-format on
static_assert(std::is_same_v<
@@ -314,7 +337,7 @@ struct Animal {
friend auto boost_openmethod_vptr(const Animal&, void*) -> vptr_type;
};
static_assert(detail::has_vptr_fn<default_registry, Animal>);
static_assert(detail::has_vptr_fn<Animal, default_registry>);
} // namespace TEST_NS
@@ -330,8 +353,8 @@ struct Animal {
}
};
static_assert(detail::has_vptr_fn<test_registry, Animal>);
static_assert(!detail::has_vptr_fn<default_registry, Animal>);
static_assert(detail::has_vptr_fn<Animal, test_registry>);
static_assert(!detail::has_vptr_fn<Animal, default_registry>);
BOOST_AUTO_TEST_CASE(vptr_from_function) {
initialize<test_registry>();

View File

@@ -17,74 +17,87 @@ struct test_registry
#include <boost/openmethod.hpp>
#include <boost/openmethod/with_vptr.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/compiler.hpp>
#define BOOST_TEST_MODULE intrusive
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/output_test_stream.hpp>
#include "test_util.hpp"
namespace bom = boost::openmethod;
using bom::virtual_;
struct Animal : bom::with_vptr<Animal> {
Animal();
explicit Animal(std::ostream& os);
~Animal();
std::ostream& os;
};
struct Cat : Animal, bom::with_vptr<Cat, Animal> {
Cat();
explicit Cat(std::ostream& os);
~Cat();
};
struct Pet : bom::with_vptr<Pet> {
Pet();
explicit Pet(std::ostream& os);
~Pet();
std::string name;
std::ostream& os;
};
struct DomesticCat : Cat, Pet, bom::with_vptr<DomesticCat, Cat, Pet> {
DomesticCat();
explicit DomesticCat(std::ostream& os);
~DomesticCat();
};
BOOST_OPENMETHOD(
speak, (virtual_<const Animal&> animal, std::ostream& os), void);
BOOST_OPENMETHOD(describe, (virtual_<const Pet&> pet, std::ostream& os), void);
Animal::Animal() {
speak(*this, std::cout);
BOOST_OPENMETHOD(
meet,
(virtual_<std::shared_ptr<Animal>>,
virtual_<const std::shared_ptr<Animal>&>, std::ostream& os),
void);
BOOST_OPENMETHOD_OVERRIDE(
meet,
(std::shared_ptr<Animal>, const std::shared_ptr<Animal>&, std::ostream& os),
void) {
os << "ignore\n";
}
Animal::Animal(std::ostream& os) : os(os) {
speak(*this, os);
}
Animal::~Animal() {
speak(*this, std::cout);
speak(*this, os);
}
Cat::Cat() {
speak(*this, std::cout);
Cat::Cat(std::ostream& os) : Animal(os) {
speak(*this, os);
}
Cat::~Cat() {
speak(*this, std::cout);
speak(*this, os);
}
Pet::Pet() {
describe(*this, std::cout);
Pet::Pet(std::ostream& os) : os(os) {
describe(*this, os);
}
Pet::~Pet() {
describe(*this, std::cout);
describe(*this, os);
}
DomesticCat::DomesticCat() {
DomesticCat::DomesticCat(std::ostream& os) : Cat(os), Pet(os) {
name = "Felix";
describe(*this, std::cout);
describe(*this, os);
}
DomesticCat::~DomesticCat() {
name = "Felix";
describe(*this, std::cout);
describe(*this, Cat::os);
}
BOOST_OPENMETHOD_OVERRIDE(speak, (const Animal&, std::ostream& os), void) {
@@ -117,56 +130,53 @@ BOOST_OPENMETHOD_OVERRIDE(
BOOST_AUTO_TEST_CASE(intrusive_mode) {
bom::initialize();
std::unique_ptr<DomesticCat> cat;
std::shared_ptr<DomesticCat> cat;
std::ostringstream cd_output;
{
boost::test_tools::output_test_stream output;
{
capture_cout capture(output.rdbuf());
cat = std::make_unique<DomesticCat>();
}
std::ostringstream output;
cat = std::make_shared<DomesticCat>(cd_output);
BOOST_CHECK(output.is_equal(
BOOST_TEST(
cd_output.str() ==
"???\n"
"meow\n"
"I am a pet\n"
"I am Felix the cat\n"));
"I am Felix the cat\n");
}
{
boost::test_tools::output_test_stream output;
{
capture_cout capture(output.rdbuf());
describe(*cat, std::cout);
}
BOOST_CHECK(output.is_equal("I am Felix the cat\n"));
std::ostringstream output;
describe(*cat, output);
BOOST_TEST(output.str() == "I am Felix the cat\n");
}
{
boost::test_tools::output_test_stream output;
{
capture_cout capture(output.rdbuf());
cat_influencer(*cat, std::cout);
}
BOOST_CHECK(output.is_equal("Follow Felix the cat on YouTube\n"));
std::ostringstream output;
cat_influencer(*cat, output);
BOOST_TEST(output.str() == "Follow Felix the cat on YouTube\n");
}
{
boost::test_tools::output_test_stream output;
{
capture_cout capture(output.rdbuf());
cat.reset();
}
cd_output.str("");
cat.reset();
BOOST_CHECK(output.is_equal(
BOOST_TEST(
cd_output.str() ==
"I am Felix the cat\n"
"I am a pet\n"
"meow\n"
"???\n"));
"???\n");
}
{
auto felix = std::make_shared<Cat>(cd_output),
sylvester = std::make_shared<Cat>(cd_output);
std::ostringstream output;
meet(felix, sylvester, output);
BOOST_TEST(output.str() == "ignore\n");
}
}