Skip to content

Latest commit

 

History

History
285 lines (191 loc) · 7.23 KB

SPEC.md

File metadata and controls

285 lines (191 loc) · 7.23 KB

Specification

This document specifies all rules bindgen uses to do conversions. All have an assigned paragraph number for easy referencing.

Rules should have one (or more) examples, showcasing its implications.

§1. C/C++ Parsing

§2. Crystal wrappers

§2.1 Name rewriting

Methods for which no other sub-rule applies have their name underscored: addWidget() -> #add_widget

§2.1.1 Getters

A getter method is a method which:

  1. Returns something different than C++ void
  2. Has no arguments
  3. Starts with get

If those rules are fulfilled, then the get prefix is removed: getWindowTitle() -> #window_title

This rule can be overriden by §2.1.2 Boolean getters.

§2.1.2 Boolean getters

A boolean getter is a method which:

  1. Returns C++ bool
  2. Has no arguments
  3. Starts with get or is or has

If these rules are fulfilled, then:

  • get prefix is removed: getAwesome() -> #awesome?
  • is prefix is removed: isEmpty() -> #empty?
  • has prefix is retained: hasSpace() -> #has_space?

§2.1.3 Setters

A setter is a method which:

  1. Returns C++ void
  2. Has exactly one argument
  3. Starts with set

If these rules are fulfilled, then the set prefix is removed, and a Crystal writer method is created: setWindowTitle() -> #window_title=

§2.2 Arguments

§2.2.1 Conversion

A conversion from the wrapper towards the binding happens if any of:

  • The crystal_type and the binding_type differs
  • A from_crystal for the arguments type is configured
  • A converter for the arguments type is configured

If a conversion happens, the precedence is as follows:

  1. from_crystal is used if set
  2. converter is used if set

Examples:

  1. A type with both from_crystal and converter set would be wrapped by using the from_crystal template: my_from_crystal(arg_name).
  2. A type with converter set would be wrapped by using the converter module: MyConverter.unwrap(arg_name).

§2.2.4 Unnamed arguments

C++ allows the declaration of unnamed arguments, like this: void foo(int). To wrap these methods, such arguments are given a name like unnamed_arg_X, where X is the zero-indexed index of the affected argument.

Given void foo(int, int two, int), the generated wrapper prototype would look like this: foo(unnamed_arg_0 : Int32, two : Int32, unnamed_arg_2 : Int32).

§2.2.5 Argument renaming

If an argument has a name which is a reserved word in Crystal, it gets an underscore (_) appended: void foo(int next) -> foo(next_ : Int32).

§2.3 Inheritance and abstract classes

Inheritance from wrapped C++ classes is replicated in Crystal.

For a C++ class to be used as base-class in the Crystal wrapper, all of these have to be true:

  1. The base-class is wrapped.
  2. The base-class is publicly inherited.

§2.3.1 Single inheritance

The base-class is inherited in Crystal, mirroring the C++ definition.

class Foo : public Bar { ... };

If both Foo and Bar are wrapped, the following wrapper will be generated:

class Foo < Bar
  # ...
end

§2.3.2 Multiple inheritance

The Crystal wrapper class will inherit from the first of all base-classes. All following base-classes are wrapped through an #as_X conversion method.

class Foo : public Bar, public AnotherOne { ... };

Will generate:

class Foo < Bar
  def as_another_one : AnotherOne
    # Conversion code ...
  end

  # ...
end

§2.3.3 Abstract classes

An abstract C++ class will also be declared abstract in Crystal. Further, an implementation class is generated to aid in conversion calls later on:

class Foo { // A pure class
  virtual void doSomething() = 0;
};

Will be wrapped as:

abstract class Foo
  abstract def do_something
end

class FooImpl < Foo
  def do_something
    # Call implementation ...
  end
end

Note: The Impl class cannot be inherited from.

§2.3.4 Virtual method override

It's possible to override a C++ method in a Crystal sub-class, if the C++ method was defined as virtual. A Crystal method is considered to override a C++ virtual method if the Crystal method and the C++ method share the same name.

class Foo {
  virtual int doWork() { ... }
};

Can be overriden from Crystal like this:

class MyFoo < Foo # Inherit
  def do_work
    # ...
  end
end

The arguments and result values are proxied back and forth from C++ like normal wrappers do. The rules of argument handling is the same. Such overrides will look and behave the same to C++ as any other virtual C++ method.

§3. Crystal bindings

§3.1 Naming scheme

The bindings have to support method overloading, and as such use name mangling to generate unique method names based on a method prototype.

The name consists out of the following parts (in this order):

  1. bg prefix, marking it as bindgen wrapper function
  2. The qualified C++ class name
  3. Method type marker (See below)
  4. The C++ method name
  5. The mangled argument list (See below)

All of these parts are joined by a single underscore (_).

Argument type mangling

The argument list is a list of all types of the arguments, in the order they appear in the method prototype. Each argument types C++ name is taken and transformed, such that:

  1. A * is replaced with X
  2. A & is replaced with R
  3. All alpha-numeric characters are kept
  4. All other characters are replaced with _

Method types

The method type marker mainly depends on the method type. However, special generated methods may use custom markers to distinguish them.

  • Member methods don't have a marker
  • Static methods use STATIC
  • Constructors use CONSTRUCT
  • Destructors use DESTRUCT

Examples

  1. Member method void Foo::bar(int *&) -> bg_Foo_bar_int_XR
  2. Static method: void Foo::bar(std::string) -> bg_Foo_STATIC_bar_std__string
  3. Method without arguments: void Foo::bar() -> bg_Foo_bar_ (Trailing _)

§4. C++ wrapper functions

For each wrapped C++ function, there is at least one wrapper function generated.

§5. Enumerations

§5.1 Name rewriting

The name of an enum is constantized: enum foo_bar is translated as enum FooBar.

§5.2 Field name rewriting

The name of an enum constant is constantized: color_0 -> Color0.

§5.3 Flag types

If an enumeration type is signaled to be a flags type, it'll get a @[Flags] annotation in Crystal wrapper code.

Given a QFlags<ApplicationFlag>, the generated enumeration will look like

@[Flags]
enum ApplicationFlag : Int32
  # Constants ...
end

§Q. Qt specifics

§Q.1 QObject signals

QObject signals are wrapped in two methods: First, the emission method, and second the connection method, which are then exposed as Crystal wrappers.

  • Given this signal: void changed(int one)
  • Emission method: changed(one : Int32) : Void
  • Connection method: on_changed(&block : Int32 -> Void)

§Q.1.1

The connection method returns an connection object, which responds to #disconnect. Calling this method breaks the connection. Subsequent calls to this method have no effect.

§Q.2 QFlags

QFlags<EnumT> is a C++/Qt template type marking a C++ enum to be a flags type. Once detected, the clang tool will mark the enum type as a flags type. See §5.3 Flag types.