mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 04:22:12 +00:00
namespaces and headers tutorial
This commit is contained in:
@@ -15,8 +15,6 @@ BOOST_OPENMETHOD(NAME, (PARAMETERS...), RETURN_TYPE [, POLICY]);
|
||||
|
||||
Declares a method.
|
||||
|
||||
### Effects
|
||||
|
||||
The macro expands to several constructs:
|
||||
|
||||
* A `struct` forward declaration that acts as the method's identifier:
|
||||
@@ -38,6 +36,8 @@ auto BOOST_OPENMETHOD_NAME(NAME)_guide(...)
|
||||
BOOST_OPENMETHOD_NAME(NAME)(PARAMETERS...), RETURN_TYPE [, POLICY]>;
|
||||
```
|
||||
|
||||
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.
|
||||
|
||||
NOTE: The default value for `POLICY` is the value of
|
||||
`BOOST_OPENMETHOD_DEFAULT_POLICY` at the point `<boost/openmethod/core.hpp>` is
|
||||
included. Changing the value of this symbol has no effect after that point.
|
||||
|
||||
@@ -17,3 +17,5 @@ BOOST_OPENMETHOD_INLINE_OVERRIDE(NAME, (PARAMETERS...), RETURN_TYPE) {
|
||||
|
||||
`BOOST_OPENMETHOD_INLINE_OVERRIDE` performs the same function as
|
||||
`BOOST_OPENMETHOD_OVERRIDE`, except that the overrider is defined inline.
|
||||
|
||||
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.
|
||||
|
||||
@@ -57,3 +57,5 @@ auto BOOST_OPENMETHOD_OVERRIDERS(NAME)<RETURN_TYPE(PARAMETERS...)>::fn(
|
||||
```
|
||||
|
||||
The block following the call to the macro is the body of the function.
|
||||
|
||||
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
## Friendship
|
||||
|
||||
Overriders are implemented as static functions located in specializations of a
|
||||
template named after the method, declared in the same scope. Macro
|
||||
`BOOST_OPENMETHOD_OVERRIDERS` returns that name. The template argument for a
|
||||
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:
|
||||
|
||||
@@ -20,20 +20,12 @@ We can thus grant friendship to all the overriders of `poke`:
|
||||
include::{examplesdir}/friendship.cpp[tag=friend_all]
|
||||
----
|
||||
|
||||
Be aware, though, that the overriders of _any_ method called `poke` - even with
|
||||
a different signature - are granted friendship.
|
||||
Be aware, though, that the overriders of _any_ method called `poke` - with any
|
||||
signature - are granted friendship.
|
||||
|
||||
We can also grant friendship to individual overriders:
|
||||
We can also befriendto individual overriders:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{examplesdir}/friendship.cpp[tag=friend]
|
||||
----
|
||||
|
||||
// If the overriders exist in a different namespace, we must take into account that
|
||||
// the overriders template is declared in the current namespace.
|
||||
|
||||
// [source,c++]
|
||||
// ----
|
||||
// include::{examplesdir}/friendship_across_namespaces.cpp[tag=friend]
|
||||
// ----
|
||||
|
||||
171
doc/headers_namespaces.adoc
Normal file
171
doc/headers_namespaces.adoc
Normal file
@@ -0,0 +1,171 @@
|
||||
|
||||
## Headers and Namespaces
|
||||
|
||||
Most real-life programs will be organized in multiple files and multiple
|
||||
namespaces. OpenMethod interacts with headers and namespaces very naturally,
|
||||
unless 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::{examplesdir}/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, in the `felines` namespace:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{examplesdir}/headers_namespaces/cat.hpp[]
|
||||
----
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{examplesdir}/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 multiple copies of the same
|
||||
class data will be created. It doesn't matter which namespace the macro is
|
||||
called in. It can take 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 this
|
||||
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.
|
||||
|
||||
Let's implement the `Dog` class, in the `canines` namespace:
|
||||
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{examplesdir}/headers_namespaces/dog.hpp[]
|
||||
----
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{examplesdir}/headers_namespaces/dog.cpp[]
|
||||
----
|
||||
|
||||
`BOOST_OPENMETHOD_INLINE_OVERRIDE` works like `BOOST_OPENMETHOD_OVERRIDE`, but
|
||||
it can be used in a header file, because it defines the `fn` function inline.
|
||||
|
||||
|
||||
Let's look at the main program now. It derived `Bullgod` from `Dog` and provides
|
||||
an overrider for the new class:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{examplesdir}/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 canides and felides 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. And the result will be an
|
||||
invalid declaration.
|
||||
|
||||
All we need to do is to make `BOOST_OPENMETHOD_OVERRIDE` "see" the guide
|
||||
function. Its name is produced by macro `BOOST_OPENMETHOD_GUIDE(NAME)`. Thus 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";
|
||||
}
|
||||
```
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
include::hello_world.adoc[]
|
||||
include::multiple_dispatch.adoc[]
|
||||
include::headers_namespaces.adoc[]
|
||||
include::friendship.adoc[]
|
||||
include::performance.adoc[]
|
||||
include::smart_pointers.adoc[]
|
||||
|
||||
@@ -117,3 +117,5 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(dl_shared Boost::openmethod)
|
||||
add_test(NAME dlopen COMMAND dl_main)
|
||||
endif()
|
||||
|
||||
add_subdirectory(headers_namespaces)
|
||||
|
||||
8
examples/headers_namespaces/CMakeLists.txt
Normal file
8
examples/headers_namespaces/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2018-2024 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)
|
||||
|
||||
add_executable(headers_namespaces main.cpp cat.cpp dog.cpp)
|
||||
target_link_libraries(headers_namespaces Boost::openmethod)
|
||||
add_test(NAME headers_namespaces COMMAND headers_namespaces)
|
||||
22
examples/headers_namespaces/animal.hpp
Normal file
22
examples/headers_namespaces/animal.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// animal.hpp
|
||||
|
||||
#ifndef ANIMAL_HPP
|
||||
#define ANIMAL_HPP
|
||||
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace animals {
|
||||
|
||||
struct Animal {
|
||||
Animal(std::string name) : name(name) {
|
||||
}
|
||||
std::string name;
|
||||
virtual ~Animal() = default;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD(poke, (std::ostream&, virtual_ptr<Animal>), void);
|
||||
|
||||
} // namespace animals
|
||||
|
||||
#endif // ANIMAL_HPP
|
||||
17
examples/headers_namespaces/cat.cpp
Normal file
17
examples/headers_namespaces/cat.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// cat.cpp
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
#include "cat.hpp"
|
||||
|
||||
namespace felines {
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(animals::Animal, Cat);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
poke, (std::ostream & os, virtual_ptr<Cat> cat), void) {
|
||||
os << cat->name << " hisses";
|
||||
}
|
||||
|
||||
}
|
||||
16
examples/headers_namespaces/cat.hpp
Normal file
16
examples/headers_namespaces/cat.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// felines.hpp
|
||||
|
||||
#ifndef FELINES_HPP
|
||||
#define FELINES_HPP
|
||||
|
||||
#include "animal.hpp"
|
||||
|
||||
namespace felines {
|
||||
|
||||
struct Cat : animals::Animal {
|
||||
using Animal::Animal;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FELINES_HPP
|
||||
10
examples/headers_namespaces/dog.cpp
Normal file
10
examples/headers_namespaces/dog.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <iostream>
|
||||
#include<boost/openmethod.hpp>
|
||||
|
||||
#include "dog.hpp"
|
||||
|
||||
namespace canines {
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(animals::Animal, Dog);
|
||||
|
||||
}
|
||||
22
examples/headers_namespaces/dog.hpp
Normal file
22
examples/headers_namespaces/dog.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef CANINES_HPP
|
||||
#define CANINES_HPP
|
||||
|
||||
#include <iosfwd>
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
#include "animal.hpp"
|
||||
|
||||
namespace canines {
|
||||
|
||||
struct Dog : animals::Animal {
|
||||
using Animal::Animal;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD_INLINE_OVERRIDE(
|
||||
poke, (std::ostream & os, virtual_ptr<Dog> dog), void) {
|
||||
os << dog->name << " barks";
|
||||
}
|
||||
|
||||
} // namespace canines
|
||||
|
||||
#endif // CANINES_HPP
|
||||
38
examples/headers_namespaces/main.cpp
Normal file
38
examples/headers_namespaces/main.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <iostream>
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/compiler.hpp>
|
||||
|
||||
#include "animal.hpp"
|
||||
#include "cat.hpp"
|
||||
#include "dog.hpp"
|
||||
|
||||
struct Bulldog : canines::Dog {
|
||||
using Dog::Dog;
|
||||
};
|
||||
|
||||
BOOST_OPENMETHOD_CLASSES(canines::Dog, Bulldog);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
poke, (std::ostream & os, virtual_ptr<Bulldog> dog), void) {
|
||||
next(os, dog);
|
||||
os << " and bites back";
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
std::unique_ptr<animals::Animal> felix(new felines::Cat("Felix"));
|
||||
std::unique_ptr<animals::Animal> snoopy(new canines::Dog("Snoopy"));
|
||||
std::unique_ptr<animals::Animal> hector(new Bulldog("Hector"));
|
||||
|
||||
poke(std::cout, *felix); // Felix hisses
|
||||
std::cout << ".\n";
|
||||
|
||||
poke(std::cout, *snoopy); // Snoopy barks
|
||||
std::cout << ".\n";
|
||||
|
||||
poke(std::cout, *hector); // Hector barks and bites
|
||||
std::cout << ".\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
23
examples/headers_namespaces/main_unrelated_namespaces.cpp
Normal file
23
examples/headers_namespaces/main_unrelated_namespaces.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <iostream>
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/compiler.hpp>
|
||||
|
||||
#include "animal.hpp"
|
||||
#include "cat.hpp"
|
||||
#include "dog.hpp"
|
||||
|
||||
using animals::Animal;
|
||||
|
||||
namespace app_specific_behavior {
|
||||
|
||||
BOOST_OPENMETHOD(
|
||||
meet, (std::ostream&, virtual_ptr<Animal>, virtual_ptr<Animal>), void);
|
||||
|
||||
}
|
||||
|
||||
using app_specific_behavior::BOOST_OPENMETHOD_GUIDE(meet);
|
||||
|
||||
BOOST_OPENMETHOD_OVERRIDE(
|
||||
meet, (std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
|
||||
os << "ignore";
|
||||
}
|
||||
42
examples/headers_namespaces/main_using_directive.cpp
Normal file
42
examples/headers_namespaces/main_using_directive.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <iostream>
|
||||
#include <boost/openmethod.hpp>
|
||||
#include <boost/openmethod/compiler.hpp>
|
||||
|
||||
#include "animal.hpp"
|
||||
#include "cat.hpp"
|
||||
#include "dog.hpp"
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
boost::openmethod::initialize();
|
||||
|
||||
std::unique_ptr<animals::Animal> felix(new Cat("Felix"));
|
||||
std::unique_ptr<animals::Animal> snoopy(new Dog("Snoopy"));
|
||||
std::unique_ptr<animals::Animal> hector(new Bulldog("Hector"));
|
||||
|
||||
poke(std::cout, *felix); // Felix hisses
|
||||
std::cout << ".\n";
|
||||
|
||||
poke(std::cout, *snoopy); // Snoopy barks
|
||||
std::cout << ".\n";
|
||||
|
||||
poke(std::cout, *hector); // Hector barks and bites
|
||||
std::cout << ".\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user