haxe
haxe copied to clipboard
[cpp] Add compiler metadata to append/prepend content to type/field definitions in header files
This pull seeks to provide additional header-file configuration for the Haxe/C++ target by adding three built-in metadatas: @:headerDefinitionPrepend, @:headerDefinitionAppend, and @:headerClassNamePrepend.
@:headerDefinitionPrepend places text directly before the definition of a class, enum, or field in the C++ header file.
@:headerDefinitionAppend does the same, only placing text directly after.
@:headerClassNamePrepend places text between the class keyword and the class name in the header file.
EDIT:
Added @:pragmaOnce; it does what it sounds like (replaces header macro guards with #pragma once). Useful because Unreal Engine requires #pragma once (doesn't like UCLASSES wrapped in macro conditionals). Since the motivation is the same (provide greater control over the Haxe/C++ header output for compatibility), hope it isn't too much trouble including it in the same pull.
Example
@:headerDefinitionPrepend("[[nodiscard]]")
function clear(arr: Array<Int>) {
var result = arr;
while(result.length > 0) result.pop();
return result;
}
// include/_Main/Main_Fields_.h
[[nodiscard]]
static ::Array< int > clear(::Array< int > arr);
Usefulness in C++ Frameworks
While the example above demonstrates how these features can be used with modern C++ attributes, the main motivation behind this pull is to provide much better support for C++ frameworks that use custom header formats. Specifically Unreal Engine and QT.
Despite now being unmaintained, Unreal.hx has amassed 400+ stars; being able to use Haxe in Unreal is clearly something a decent amount of people seek. Valid Unreal headers require an auto-generated header file to be included, and a specific macro in the class body. Fortunately, these things can be achieved using existing Haxe/C++ metadata (@:headerClassCode, @:headerInclude, etc.), but one feature is missing: annotating class members using the Unreal properties (UPROPERTY/UFUNCTION). @:headerDefinitionPrepend fixes this by allowing the text to be appended per each class member, turning what would normally require a complex build process for generating Unreal header files, into a simple metadata.
@:nativeGen
@:headerDefinitionPrepend("UCLASS()")
class MyActor extends AActor {
@:headerDefinitionPrepend("UPROPERTY(EditAnywhere)")
var string: TString;
}
Qt is another example that requires additional compiler features if Haxe/C++ hopes to generate source files for it. In addition to annotating header class members with public: and private:, Qt provides the signals: and public slots: annotations for their signal/slot system. The metadata provided in this pull can also allow for custom annotations, like so:
@:headerDefinitionPrepend("public slots:")
@:headerDefinitionAppend("public:") // return to normal after this field
public function onSignalEmitted() { /* do something */ }
Usefulness in Native Conditional Compilation and Attributes
As demonstrated in the first example, native C++ attributes can be attached to specific class members using these metadata. However, these metadata are also helpful for wrapping class members in C++ compiler conditionals (#ifdef/#endif). This can be extremely helpful in circumstances where the Haxe compiler may not have the information needed.
// in some class with @:nativeGen so this method isn't used in any reflection
@:headerDefinitionPrepend("#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)")
@:headerDefinitionAppend("#endif")
public function doSomethingQt4() { /* do something */ }
Usefulness for Native Documentation
Finally, these metadata can also be helpful for generating comments next to specific member definitions in the C++ output. I personally don't see a use for this, but I have definitely seen a demand for comment output in Haxe before. These metadata can also help with it.