Draft:YOMM2

From Wikipedia, the free encyclopedia
  • Comment: The only source is on the paper in which this is inspired by, nothing about the subject itself. Karnataka (talk) 21:55, 27 June 2023 (UTC)

This user has publicly declared that they have a conflict of interest regarding the Wikipedia article YOMM2.
YOMM2
Developer(s)Jean-Louis Leroy
Stable release
1.3.1 / January 9, 2023; 16 months ago (2023-01-09)
Written inC++17
Operating systemCross-platform
TypeLanguage extensions
LicenseBoost Software License
Websitehttps://github.com/jll63/yomm2

The YOMM2 library implements open multi-methods for C++17 and above.

Bjarne Stroustrup gave CLOS open multi-methods as an example of a feature from another language that he likes. He talks about them in Design and Evolution of C++, and suggests a syntax. Later, he and his students co-wrote a series of papers on the subject.

Overview[edit]

The declare_method macro declares a method. It is equivalent to defgeneric in the Common Lisp Object System. It takes a return type, a method name, and a list of types, some of which are marked as virtual (using the virtual_ decorator).

The define_method macro adds definitions, or specializations, to a previously declared method. It is equivalent to defgeneric in CLOS. It takes a return type, a method name, a parameter list, and is followed by a function body.

When a method is called, the dynamic types of the arguments corresponding to the virtual parameters of the method are used to select the appropriate definition to call, from the set of registered definitions. The selection is made according to the same rules as overload resolution, albeit it happens when the program is run, not compiled.

All classes potentially involved in a method call must be registered via the macro use_classes.

The yorel::yomm2::update_methods must be called before making calls to methods.

YOMM2 also provides a core interface, which does not use macros, and thus makes it possible to use the library in conjunction with templates.

Example[edit]

// =============================================================================
// Define a few polymorphic classes...

class Animal {
  public:
    virtual ~Animal() {
    }
};

class Dog : public Animal {};
class Bulldog : public Dog {};
class Cat : public Animal {};
class Dolphin : public Animal {};

// =============================================================================
// Add behavior to existing classes, without modifying them.

#include <yorel/yomm2/keywords.hpp>

// Classes must be registered:
register_classes(Animal, Dog, Cat, Dolphin);

// ...but it does not have to be in one call to 'register_classes', as long as
// inheritance relationships can be deduced. This allows *adding* classes to an
// existing collection of classes.
register_classes(Dog, Bulldog);

// Define a uni-method, i.e. a method with a single virtual argument. This is in
// essence a virtual function implemented as a free function.
declare_method(void, kick, (virtual_<Animal&>, std::ostream&));

// Implement 'kick' for dogs.
define_method(void, kick, (Dog& dog, std::ostream& os)) {
    os << "bark";
}

// Implement 'kick' for bulldogs. They behave like Dogs, but, in addition, they
// fight back.
define_method(void, kick, (Bulldog& dog, std::ostream& os)) {
    next(dog, os); // calls "base" method, i.e. definition for Dog
    os << " and bite";
}

// A multi-method with two virtual arguments...
declare_method(
    void, meet, (virtual_<Animal&>, virtual_<Animal&>, std::ostream&));

// 'meet' catch-all implementation.
define_method(void, meet, (Animal&, Animal&, std::ostream& os)) {
    os << "ignore";
}

// Add definitions for specific pairs of animals.
define_method(void, meet, (Dog& dog1, Dog& dog2, std::ostream& os)) {
    os << "wag tail";
}

define_method(void, meet, (Dog& dog, Cat& cat, std::ostream& os)) {
    os << "chase";
}

define_method(void, meet, (Cat& cat, Dog& dog, std::ostream& os)) {
    os << "run";
}

// =============================================================================
// main

#include <iostream>
#include <memory>

int main() {
    // Initialise method dispatch tables.
    yorel::yomm2::update_methods();

    // Create a few objects.
    // Note that the actual classes are type-erased to base class Animal!
    std::unique_ptr<Animal>
        hector = std::make_unique<Bulldog>(),
        snoopy = std::make_unique<Dog>(),
        sylvester = std::make_unique<Cat>(),
        flipper = std::make_unique<Dolphin>();

    // Call 'kick'.
    std::cout << "kick snoopy: ";
    kick(*snoopy, std::cout); // bark
    std::cout << "\n";

    std::cout << "kick hector: ";
    kick(*hector, std::cout); // bark and bite
    std::cout << "\n";

    // Call 'meet'.
    std::cout << "hector meets sylvester: ";
    meet(*hector, *sylvester, std::cout); // chase
    std::cout << "\n";

    std::cout << "sylvester meets hector: ";
    meet(*sylvester, *hector, std::cout); // run
    std::cout << "\n";

    std::cout << "hector meets snoopy: ";
    meet(*hector, *snoopy, std::cout); // wag tail
    std::cout << "\n";

    std::cout << "hector meets flipper: ";
    meet(*hector, *flipper, std::cout); // ignore
    std::cout << "\n";
}

Design[edit]

The library is inspired by the paper "Open Multi-Methods for C++", by Peter Pirkelbauer, Yuryi Solodkyy and Bjarne Stroustrup [1] .

At compilation time, YOMM2 uses macro and template meta-programming to collect information about classes, methods, and method definitions, and stores it in static objects.

At initialization time, YOMM2 calculates the inheritance relationships between the registered classes, and, for each method, which definition should be called for every possible tuple of virtual arguments. For each class, it builds a method table. For uni-methods, it contains a pointer to the applicable definition. For multi-methods, it contains a pointer to a multi-dimensional dispatch table, which is free of redundancies. YOMM2 also finds a perfect hash function for the typeid of the registered classes, and uses it to build a hash table that maps typeids to method tables.

At dispatch time, YOMM2 reads the dynamic typeid of the virtual arguments, and uses the hash table to retrieve the corresponding method tables. In release builds, a method call is very fast: calling a uni-method is almost as fast as calling an equivalent virtual function. In debug builds, YOMM2 performs various checks to detect and clearly report errors such as missing or ambiguous definitions, or missing class registrations.

References[edit]

  1. ^ Pirkelbauer, Peter; Solodkyy, Yuryi; Stroustrup, Bjarne. "Open Multi-Methods for C++" (PDF).

External links[edit]


Category:Free software programmed in C++ Category:C++ libraries Category:Articles with example C++ code