<jupyter_start><jupyter_text>Cppyy Tutorial_(Modified from <NAME>'s cppyy tutorial.)_This tutorial introduces the basic concepts for using cppyy, the automatic Python-C++ generator. To install cppyy on your system, simply run (this may take a while as it will pull in and compile a custom version of LLVM):```$ pip install cppyy```For further details on the installation, as well as the location of binary wheels, see:   http://cppyy.readthedocs.io/en/latest/installation.htmlTo start, import module cppyy. All functionality, including using bound classes, always starts at this top-level.<jupyter_code>import cppyy<jupyter_output><empty_output><jupyter_text>There are three layers to cppyy: at the top there are the module 'gbl' (the _global_ namespace), a range of helper functions, and a set of sub-modules (such as `py`) that serve specific purposes. Let's start with defining a little helper class in C++ using the helper function `cppdef`, to make the example more interesting:<jupyter_code>cppyy.cppdef("""
class Integer1 {
public:
    Integer1(int i) : m_data(i) {}
    int m_data;
};""")<jupyter_output><empty_output><jupyter_text>We now have a class 'Integer1'. Note that this class exists on the C++ side and has to follow C++ rules. For example, whereas in Python we can simply redefine a class, we can't do that in C++. Therefore, we will number the `Integer` classes as we go along, to be able to extend the example as we see fit.Python classes are constructed dynamically. It doesn't matter where or how they are defined, whether in a Python script, "compiled" into a C extension module, or otherwise. Cppyy takes advantage of this fact to generate bindings on-the-fly. This leads to performance advantages for large libraries with thousands of C++ classes; general distribution advantages since, other than the module cppyy itself, no code depends on any specific version of Python; and it enablers, through the Cling backend, interactive access to C++.To access our first class, find it in gbl, the global namespace:<jupyter_code>print(cppyy.gbl.Integer1)<jupyter_output><class cppyy.gbl.Integer1 at 0x1a426c0><jupyter_text>Namespaces have simularities to modules, so we could have imported the class as well.Bound C++ classes are first-class Python object. We can instantiate them, use normal Python introspection tools, call `help()`, they raise Python exceptions on failure, manage memory through Python's ref-counting and garbage collection, etc., etc. Furthermore, we can use them in conjunction with other C++ classes.<jupyter_code># for convenience, bring Integer1 into __main__
from cppyy.gbl import Integer1

# create a C++ Integer1 object
i = Integer1(42)

# use Python inspection
print("Variable has an 'm_data' data member?", hasattr(i, 'm_data') and 'Yes!' or 'No!')
print("Variable is an instance of int?", isinstance(i, int) and 'Yes!' or 'No!')
print("Variable is an instance of Integer1?", isinstance(i, Integer1) and 'Yes!' or 'No!')
# pull in the STL vector class
from cppyy.gbl.std import vector

# create a vector of Integer1 objects; note how [] instantiates the template and () instantiates the class
v = vector[Integer1]()

# populate it
v += [Integer1(j) for j in range(10)]

# display our vector
print(v)<jupyter_output><cppyy.gbl.std.vector<Integer1> object at 0x336ddb0><jupyter_text>Hum, that doesn't look very pretty. However, since Integer1 is now a Python class we can decorate it, with a custom `__repr__` function (we'll punt on the `vector` and instead convert it to a Python `list` for printing).<jupyter_code># add a custom conversion for printing
Integer1.__repr__ = lambda self: repr(self.m_data)

# now try again (note the conversion of the vector to a Python list)
print(list(v))<jupyter_output>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]<jupyter_text>PythonizationsAs we have seen so far, automatic bindings are simple and easy to use. However, even though they are first-class Python objects, they do have some rough C++ edges left. There is some _pythonization_ going on in the background: the vector, for example, played nice with `+=` and the list conversion. But for presenting your own classes to end-users, specific pythonizations are desirable. To have this work correctly with lazy binding, a callback-based API exists.Now, it's too late for Integer1, so let's create Integer2, which lives in a namespace and in addition has a conversion feature.<jupyter_code># create an Integer2 class, living in namespace Math
cppyy.cppdef("""
namespace Math {
    class Integer2 : public Integer1 {
    public:
        using Integer1::Integer1;
        operator int() { return m_data; }
    };
}""")
# prepare a pythonizor
def pythonizor(klass, name):
    # A pythonizor receives the freshly prepared bound C++ class, and a name stripped down to
    # the namespace the pythonizor is applied. Also accessible are klass.__name__ (for the
    # Python name) and klass.__cpp_name__ (for the C++ name)
    if name == 'Integer2':
        klass.__repr__ = lambda self: repr(self.m_data)

# install the pythonizor as a callback on namespace 'Math' (default is the global namespace)
cppyy.py.add_pythonization(pythonizor, 'Math')
# when we next get the Integer2 class, it will have been decorated
Integer2 = cppyy.gbl.Math.Integer2    # first time a new namespace is used, it can not be imported from
v2 = vector[Integer2]()
v2 += [Integer2(j) for j in range(10)]

# now test the effect of the pythonizor:
print(list(v2))

# in addition, Integer2 has a conversion function, which is automatically recognized and pythonized
i2 = Integer2(13)
print("Converted Integer2 variable:", int(i2))
# continue the decoration on the C++ side, by adding an operator+ overload
cppyy.cppdef("""
namespace Math {
    Integer2 operator+(const Integer2& left, const Integer1& right) {
        return left.m_data + right.m_data;
    }
}""")
# now use that fresh decoration (it will be located and bound on use):
k = i2 + i
print(k, i2.m_data + i.m_data)<jupyter_output>55 55<jupyter_text>Class HierarchiesBoth Python and C++ support multiple programming paradigms, making it relatively straightforward to map language features (e.g. class inheritance, free functions, etc.); many other features can be cleanly hidden, merely because the syntax is very similar or otherwise natural (e.g. overloading, abstract classes, static data members, etc.); and yet others map gracefully because their semantic intent is expressed clearly in the syntax (e.g. smart pointers, STL, etc.).The following presents a range of C++ features that map naturally, and exercises them in Python.<jupyter_code># create some animals to play with
cppyy.cppdef("""
namespace Zoo {

    enum EAnimal { eLion, eMouse };
    
    class Animal {
    public:
        virtual ~Animal() {}
        virtual std::string make_sound() = 0;
    };
    
    class Lion : public Animal {
    public:
        virtual std::string make_sound() { return s_lion_sound; }
        static std::string s_lion_sound;
    };
    std::string Lion::s_lion_sound = "growl!";

    class Mouse : public Animal {
    public:
        virtual std::string make_sound() { return "peep!"; }
    };

    Animal* release_animal(EAnimal animal) {
        if (animal == eLion) return new Lion{};
        if (animal == eMouse) return new Mouse{};
        return nullptr;
    }

    std::string identify_animal(Lion*) {
        return "the animal is a lion";
    }

    std::string identify_animal(Mouse*) {
        return "the animal is a mouse";
    }

}
""")

# pull in the Zoo (after which we can import from it)
Zoo = cppyy.gbl.Zoo

# pythonize the animal release function to take ownership on return
Zoo.release_animal.__creates__ = True
# abstract base classes can not be instantiated:
try:
    animal = Zoo.Animal()
except TypeError as e:
    print('Failed:', e, '\n')

# derived classes can be inspected in the same class hierarchy on the Python side
print('A Lion is an Animal?', issubclass(Zoo.Lion, Zoo.Animal) and 'Yes!' or 'No!', '\n')

# returned pointer types are auto-casted to the lowest known derived type:
mouse = Zoo.release_animal(Zoo.eMouse)
print('Type of mouse:', type(mouse))
lion = Zoo.release_animal(Zoo.eLion)
print('Type of lion:', type(lion), '\n')

# as pythonized, the ownership of the return value from release_animal is Python's
print("Does Python own the 'lion'?", lion.__python_owns__ and 'Yes!' or 'No!')
print("Does Python own the 'mouse'?", mouse.__python_owns__ and 'Yes!' or 'No!', '\n')

# virtual functions work as expected:
print('The mouse says:', mouse.make_sound())
print('The lion says:', lion.make_sound(), '\n')

# now change what the lion says through its static (class) variable
Zoo.Lion.s_lion_sound = "mooh!"
print('The lion says:', lion.make_sound(), '\n')

# overloads are combined into a single function on the Python side and resolved dynamically
print("Identification of \'mouse\':", Zoo.identify_animal(mouse))
print("Identification of \'lion\':", Zoo.identify_animal(lion))<jupyter_output>Failed: none of the 2 overloaded methods succeeded. Full details:
  cannot instantiate abstract class 'Zoo::Animal'
  cannot instantiate abstract class 'Zoo::Animal' 

A Lion is an Animal? Yes! 

Type of mouse: <class cppyy.gbl.Zoo.Mouse at 0x24ea2f0>
Type of lion: <class cppyy.gbl.Zoo.Lion at 0x37c1990> 

Does Python own the 'lion'? Yes!
Does Python own the 'mouse'? Yes! 

The mouse says: peep!
The lion says: growl! 

The lion says: mooh! 

Identification of 'mouse': the animal is a mouse
Identification of 'lion': the animal is a lion<jupyter_text>Modern C++As C++ matures, more and more semantic intent (such as object ownership) is expressed in the syntax. This not for the benefit of bindings generators, but for the poor programmer having to read the code. Still, a bindings generator benefits greatly from this increased expression.<jupyter_code>cppyy.cppdef("""
namespace Zoo {
   std::shared_ptr<Lion> free_lion{new Lion{}};

   std::string identify_animal_smart(std::shared_ptr<Lion>& smart) {
       return "the animal is a lion";
   }
}
""")
# shared pointers are presented transparently as the wrapped type
print("Type of the 'free_lion' global:", type(Zoo.free_lion).__name__)

# if need be, the smart pointer is accessible with a helper
smart_lion = Zoo.free_lion.__smartptr__()
print("Type of the 'free_lion' smart ptr:", type(smart_lion).__name__)

# pass through functions that expect a naked pointer or smart pointer
print("Dumb passing: ", Zoo.identify_animal(Zoo.free_lion))
print("Smart passing:", Zoo.identify_animal_smart(Zoo.free_lion))<jupyter_output>Type of the 'free_lion' global: Lion
Type of the 'free_lion' smart ptr: shared_ptr<Zoo::Lion>
Dumb passing:  the animal is a lion
Smart passing: the animal is a lion
{'fraction_non_alphanumeric': 0.0942096443865202, 'fraction_numerical': 0.00837832805808974, 'mean_word_length': 4.636411332633788, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '161482', 'license': 'BSD-3-Clause-LBNL', 'n_tokens_mistral': 3259, 'n_tokens_neox': 3096, 'n_words': 1364, 'path': 'doc/tutorial/CppyyTutorial.ipynb', 'repo_name': 'ToolFramework/cppyy', 'size': '10750'}