Skip to content

02 Customizing Method Names

Anders Langlands edited this page Apr 17, 2021 · 3 revisions

A More Complex Example

Let's expand our simple Hello class to take arguments. And because it's C++ we'll implement different argument types as overloads

#pragma once

#include <iostream>

namespace hello {

class Hello {
public:
    void hello(const char* msg) const {
        std::cout << "Hello, " << msg << "!\n";
    }
    void hello(const int msg) const { std::cout << "Hello, " << msg << "!\n"; }
    void hello(const float msg) const {
        std::cout << "Hello, " << msg << "!\n";
    }
};

} // namespace hello

We'll create a new working directory and save this as 02_customizing_methods/include/hello.hpp.

Next we'll need to update our binding file to match the new method signiatures

#include "hello.hpp"

namespace cppmm_bind {

namespace hello {

struct Hello {
    using BoundType = ::hello::Hello;
    void hello(const char* msg) const;
    void hello(const int msg) const;
    void hello(const float msg) const;
};

} // namespace hello

} // namespace cppmm_bind

and we'll save this as 02_customizing_methods/bind/c-hello.cpp.

If we now generate the AST and the bindings:

./astgen/astgen 02_customizing_methods/bind/c-hello.cpp -v 1 -o 02_customizing_methods/ast -- -I../tutorial/02_customizing_methods/include
./asttoc/asttoc 02_customizing_methods/ast -o 02_customizing_methods -p customizing_methods

and inspect the resulting C header, 02_customizing_methods/customizing_methods-c/c-hello.h, we can see that asttoc has renamed the methods for us to avoid name collisions. The names aren't exactly helpful though:

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

typedef struct hello__Hello_t_s {
    char data[1];
} __attribute__((aligned(1))) hello__Hello_t;
typedef hello__Hello_t hello_Hello_t;

void hello__Hello_hello(
    hello_Hello_t const * this_
    , char const * msg);

#define hello_Hello_hello hello__Hello_hello

void hello__Hello_hello_1(
    hello_Hello_t const * this_
    , int const msg);

#define hello_Hello_hello_1 hello__Hello_hello_1

void hello__Hello_hello_2(
    hello_Hello_t const * this_
    , float const msg);

#define hello_Hello_hello_2 hello__Hello_hello_2

#ifdef __cplusplus
}
#endif

If you're wondering what those #defines and typedefs that look redundant are there for, that will be explained in tutorial 04. SPOILER: they're there to allow us to provide nice public names for all functions and types, but we haven't set that up yet.

Customizing the Method Names

Fortunately, astgen lets us add attributes to the binding file to tweak the generated binding in a number of ways, including renaming methods. This is done with standard clang/gcc attributes, and they're wrapped up in macros to make them easier to use.

These macros are defined in a virtual file that's injected by astgen automatically, called cppmm_bind.hpp, so the first thing to do is to modify the binding file to include this virtual header, and then we can use the CPPMM_RENAME macro to specify what name we want for each overload. For example:

#include "hello.hpp"

#include <cppmm_bind.hpp>

namespace cppmm_bind {

namespace hello {

struct Hello {
    using BoundType = ::hello::Hello;
    void hello(const char* msg) const CPPMM_RENAME(hello_string);
    void hello(const int msg) const CPPMM_RENAME(hello_int);
    void hello(const float msg) const CPPMM_RENAME(hello_float);
};

} // namespace hello

} // namespace cppmm_bind

and re-running the ast and binding generation as above gives us

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

typedef struct hello__Hello_t_s {
    char data[1];
} __attribute__((aligned(1))) hello__Hello_t;
typedef hello__Hello_t hello_Hello_t;

void hello__Hello_hello_string(
    hello_Hello_t const * this_
    , char const * msg);

#define hello_Hello_hello_string hello__Hello_hello_string

void hello__Hello_hello_int(
    hello_Hello_t const * this_
    , int const msg);

#define hello_Hello_hello_int hello__Hello_hello_int

void hello__Hello_hello_float(
    hello_Hello_t const * this_
    , float const msg);

#define hello_Hello_hello_float hello__Hello_hello_float

#ifdef __cplusplus
}
#endif

Much better!