Compare commits

..

12 Commits

Author SHA1 Message Date
91ddcd4f79 improve README 2026-06-07 19:14:45 +02:00
a8150171fc add README 2026-06-07 19:06:09 +02:00
69ed4727dd remove cxx builder 2026-06-07 18:37:52 +02:00
57b94c558e Merge branch 'main' of https://git.unicon-gmbh.de/BeefBindings/Cpp2Beef 2026-05-28 15:50:11 +02:00
0bb0a4e4fc increase custamizability 2026-05-28 15:47:49 +02:00
Rune
69ed2da625 forgor protected 2026-03-13 16:49:43 +00:00
Rune
98d72bc6cd fix anon param in virtual method 2026-03-13 16:48:06 +00:00
Rune
f56fad47af step towards c++ 2026-03-11 19:50:31 +01:00
Rune
27c414501a fix wrapper ctors 2026-03-06 21:22:46 +01:00
Rune
6c648ae0cb kate butchered averything :( 2026-03-06 18:41:15 +01:00
Rune
8766c55bbb fix wrapper.cpp 2026-03-06 18:39:22 +01:00
Rune
09748b9eeb fix static block appearing before rsquirly 2026-03-06 17:22:57 +01:00
12 changed files with 157 additions and 599 deletions

View File

@@ -1,6 +0,0 @@
build
recovery
test
BeefSpace_User.toml
dist/*

View File

@@ -1,29 +0,0 @@
FileVersion = 1
[Project]
Name = "CxxBuilder"
StartupObject = "CxxBuilder.Program"
[Configs.Debug.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Debug.Win64]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Release.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Release.Win64]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Paranoid.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Paranoid.Win64]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Test.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Test.Win64]
TargetDirectory = "$(ProjectDir)/dist"

View File

@@ -1,5 +0,0 @@
FileVersion = 1
Projects = {CxxBuilder = {Path = "."}}
[Workspace]
StartupProject = "CxxBuilder"

View File

@@ -1,157 +0,0 @@
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
namespace CxxBuilder;
static class FileMatcher
{
public enum Error
{
ExpectedEscapeSequence,
CharacterClassNotClosed,
}
public static Result<void, Error> HandleMatches(Span<StringView> patterns, StringView directory, delegate void(StringView match) callback)
{
Runtime.Assert(Directory.Exists(directory), scope $"No such directory {directory}");
void Dir(StringView dir)
{
dir: for (let element in Directory.Enumerate(dir))
{
let path = element.GetFilePath(..scope .());
path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
if (element.IsDirectory)
{
Dir(path);
continue;
}
let relpath = Path.GetRelativePath(path, directory, ..scope .());
for (let pattern in patterns)
{
if (!pattern.StartsWith('^') && !pattern.StartsWith('!')) continue;
if (IsMatch(pattern[1...], relpath))
continue dir;
}
for (let pattern in patterns)
{
if (pattern.StartsWith('^') || pattern.StartsWith('!')) continue;
if (!IsMatch(pattern, relpath)) continue;
callback(relpath);
break;
}
}
}
Dir(directory);
return .Ok;
}
public static Result<bool, Error> IsMatch(StringView pattern, StringView path)
{
var pattern, path;
Result<bool, Error> Matches(StringView pattern, char8 c)
{
if (pattern.IsEmpty) return false;
switch (pattern[0])
{
case '*':
if (pattern.Length > 1 && pattern[1] == '*')
return true;
return !Path.IsDirectorySeparatorChar(c);
case '?': return true;
case '\\':
if (pattern.Length <= 1) return .Err(.ExpectedEscapeSequence);
return c == pattern[1];
case '/': return Path.IsDirectorySeparatorChar(c);
case '[':
int i = 0;
char8 Next()
{
if (pattern.Length <= ++i)
return (.)7;
return pattern[i];
}
bool negated = false;
char8 current;
switch (Next())
{
case (.)7: return .Err(.CharacterClassNotClosed);
case '^', '!': negated = true; fallthrough;
case '\\': current = Next();
case ']': return false;
default: current = _;
}
while (true)
{
if (c == current) return !negated;
switch (Next())
{
case ']': return negated;
case '-':
let end = Next();
if (c >= current && c <= end)
return !negated;
current = Next();
case '\\': current = Next();
default: current = _;
}
}
default:
return c == _;
}
}
reduce: while (true)
{
if (path.IsEmpty) return true;
if (pattern.IsEmpty) return false;
switch (Matches(pattern, path[0]))
{
case .Err: return _;
case .Ok(false): return false;
case .Ok:
}
switch (pattern[0])
{
case '*':
StringView lazy;
if (pattern.Length > 1)
{
lazy = pattern;
lazy.RemoveFromStart(pattern[1] == '*' ? 2 : 1);
}
else lazy = .();
defer { pattern = lazy; }
while (true)
{
if (Try!(IsMatch(lazy, path))) continue reduce;
if (!Try!(Matches(pattern, path[0]))) continue reduce;
path.RemoveFromStart(1);
if (path.IsEmpty) return lazy.IsEmpty;
}
case '[':
bool escaped = false;
while (true)
{
let c = pattern[0];
pattern.RemoveFromStart(1);
if (c == ']' && !escaped)
{
path.RemoveFromStart(1);
continue reduce;
}
escaped = c == '\\';
}
case '\\':
pattern.RemoveFromStart(2);
path.RemoveFromStart(1);
default:
pattern.RemoveFromStart(1);
path.RemoveFromStart(1);
}
}
}
}

View File

@@ -1,175 +0,0 @@
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
namespace CxxBuilder;
static class Program
{
[CLink] static extern int32 system(char8*);
public static int Main(String[] args)
{
mixin PrintUsage()
{
Console.WriteLine("""
Simple and fast build tool using clang and ninja.
Usage: $(Var CxxBuilderExe) [<options>] <patterns...> -- src=<> target=<> config=<> builddir=<> output=<> [cflags=<>]
Example: $(Var CxxBuilderExe) **.c vk_*.cpp -- "src=$(ProjectDir)/MyLib/src" target=$(TargetTriple) config=$(Configuration) "builddir=$(BuildDir)" output=MyLib cflags=-Isome/dir
--template - TODO
--cmake - builds $src/CMakeLists.txt to build, patterns and output are ignored
$src - The source dir, all patterns are rooted here
$target - the llvm target triple passed to clang
$config - the build configuration
$builddir - contains build artifacts and ninja files
$output - the name of the output object archive $builddir/$output.lib on windows and $builddir/$output.a elsewhere
$cflags - additional flags passed to clang
""");
return 1;
}
mixin Assert(bool condition, StringView str)
{
if (!condition)
{
Console.WriteLine(str);
return 1;
}
}
enum { Default, CMake, Template } mode = .Default;
StringView src = null, target = null, config = null, builddir = null, output = null, cflags = null;
var iter = args.GetEnumerator();
skipPatterns: do
{
for (let arg in iter)
{
if (arg == "--") break skipPatterns;
if (arg == "--cmake")
{
Assert!(mode == .Default, "Conflicting options");
mode = .CMake;
}
}
PrintUsage!();
}
for (let arg in iter)
{
var parts = arg.Split('=', 2);
StringView key = parts.GetNext().Value;
StringView value;
switch (parts.GetNext())
{
case .Err: PrintUsage!();
case .Ok(out value):
}
key.Trim();
value.Trim();
switch (key)
{
case "src": Assert!(src.IsNull, "Duplicate var 'src'"); src = value;
case "target": Assert!(target.IsNull, "Duplicate var 'target'"); target = value;
case "config": Assert!(config.IsNull, "Duplicate var 'config'"); config = value;
case "builddir": Assert!(builddir.IsNull, "Duplicate var 'builddir'"); builddir = value;
case "output": Assert!(output.IsNull, "Duplicate var 'output'"); output = value;
case "cflags": Assert!(cflags.IsNull, "Duplicate var 'cflags'"); cflags = value;
default: PrintUsage!();
}
}
Assert!(!src.IsNull, "Missing var 'src'");
Assert!(!target.IsNull, "Missing var 'target'");
Assert!(!config.IsNull, "Missing var 'config'");
Assert!(!builddir.IsNull, "Missing var 'builddir'");
Assert!(!output.IsNull || mode == .CMake, "Missing var 'output'");
switch (mode)
{
case .Default:
StreamWriter writer = scope .()..Create(scope $"{builddir}/build.ninja");
{
String buffer = scope .(1024);
let currentDir = Directory.GetCurrentDirectory(..scope .(128));
void WriteVarPath(StringView key, StringView value)
{
buffer.Append(key);
buffer.Append(" = ");
Path.GetAbsolutePath(value, currentDir, buffer);
buffer.Append('\n');
}
WriteVarPath("src", src);
WriteVarPath("builddir", builddir);
buffer.AppendF($"""
target = {target}
cflags = {(config == "Release") ? "-O2" : "-O2 -g"} {cflags}
""");
writer.Write(buffer);
}
writer.Write("""
\n
cc = clang
ar = ar
rule cc
command = $cc $cflags -target $target -MD -MF $out.d -c -o $out $in
deps = gcc
depfile = $out.d
description = Building $in
rule ar
command = $ar crs $out $in
description = Creating static lib $out
""");
String ar = scope .(1024);
ar.AppendF($"\nbuild $builddir/{output}.{target.Contains("windows") ? "lib" : "a"}: ar");
int pcount = 0;
for (let arg in args)
{
if (arg == "--") break;
pcount++;
}
StringView[] patterns = scope .[pcount];
for (var pattern in ref patterns)
{
pattern = args[@pattern];
}
void HandleMatch(StringView match)
{
writer.Write($"build $builddir/{match}.o: cc $src/{match}\n");
ar.Append(" $builddir/", match, ".o");
}
let abssrc = Path.GetAbsolutePath(src, Directory.GetCurrentDirectory(..scope .(128)), ..scope .(128));
if (FileMatcher.HandleMatches(patterns, abssrc, scope => HandleMatch) case .Err(let err))
{
Console.WriteLine($"Syntax Error in Pattern: {err}");
return 1;
}
writer.WriteLine(ar);
case .CMake:
#if !DEBUG
if (File.Exists(scope $"{builddir}/build.ninja"))
break;
#endif
String cmd = scope .(512);
cmd.Append("cmake -S \"", src, "\" -B \"", builddir, "\" -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ");
cmd.Append("-DCMAKE_C_FLAGS=\"-target ", target, " ", cflags, "\" -DCMAKE_CXX_FLAGS=\"-target ", target, " ", cflags, "\" -DCMAKE_BUILD_TYPE=");
if (config == "Release") cmd.Append("Release");
else cmd.Append("RelWithDebInfo");
Assert!(system(cmd) == 0, "Build file generation failed");
case .Template: //TODO
}
let ret = system(scope $"ninja -C {builddir}");
if (ret != 0)
Console.WriteLine($"Failed to build {output}");
return ret;
}
}

72
README.md Normal file
View File

@@ -0,0 +1,72 @@
# Cpp2Beef
Cpp2Beef is a library for writing high quality beef bindings for C and C-Style C++. Cpp2Beef does the heavy lifting so you can focus on the library specific stuff.
You can find bindings generated by Cpp2Beef at https://git.unicon-gmbh.de/BeefBindings.
## Usage
1. Create a `Setup` workspace and add Cpp2Beef from remote (`https://git.unicon-gmbh.de/Rune/Cpp2Beef.git`)
2. Add Cpp2Beef as a dependency
3. Write your generator
```beef
class ExampleGenerator : Cpp2BeefGenerator, this(Span<char8*> args)
{
protected override Span<char8*> Args => args;
protected override Flags Flags => .None;
// You can have your binding in one file or in multiple, file management is up to you
StreamWriter writer = new .()..Create("../src/Example.bf")..Write("""
// This file was generated by Cpp2Beef
using System;
using System.Interop;
namespace Example;
""") ~ delete _;
protected override StreamWriter GetWriterForHeader(StringView header)
{
// Here you define the target file to which a cursor should be written
// Returning null skips the cursor
if (header.EndsWith("example.h"))
return writer;
return null;
}
protected override Block GetCursorBlock(CXCursor cursor)
{
// Optional. This can be used put cursors in custom blocks
// This is useful if you want all your functions to be a static class
if (cursor.kind == .FunctionDecl)
return .CustomBlock("Example");
return base.GetCursorBlock(cursor);
}
protected override void HandleCursor(CXCursor cursor)
{
// Optional. Can be used to write custom logic, how a cursor should be written
// If your going for this I'd recommend looking at examples in BeefBindings
base.HandleCursor(cursor);
}
protected override void WriteCustomAttributes(CXCursor cursor)
{
// Optional.
base.WriteCustomAttributes(cursor);
}
protected override void GetNameInBindings(LibClang.CXCursor cursor, String outString)
{
// Optional but you pretty much want to always override this.
// Allows you to change the name of cursors
base.GetNameInBindings(cursor, outString);
}
// There are functions you can override, but these are the most common
}
// Create a single header file that includes all other header files and then call Generate once
// This is significanly faster than calling Generate for each header file because of header guards
scope ExampleGenerator(char8*[?]("--language=c", "-DSOMETHING")).Generate("example.h");
```

3
Setup/.gitignore vendored
View File

@@ -1,3 +0,0 @@
build
recovery
BeefSapce_user.toml

View File

@@ -1,5 +0,0 @@
FileVersion = 1
[Project]
Name = "Cpp2Beef.Setup"
StartupObject = "Cpp2Beef.Setup.Program"

View File

@@ -1,5 +0,0 @@
FileVersion = 1
Projects = {"Cpp2Beef.Setup" = {Path = "."}}
[Workspace]
StartupProject = "Cpp2Beef.Setup"

View File

@@ -1,27 +0,0 @@
using System;
using System.IO;
namespace Cpp2Beef.Setup;
static class Program
{
[CLink] static extern int32 system(char8*);
static void RunCommand(char8* cmd)
{
Console.WriteLine($"> {StringView(cmd)}");
if (system(cmd) != 0)
Runtime.FatalError("Command failed");
}
public static int Main(String[] args)
{
RunCommand("beefbuild -workspace=\"../CxxBuilder\" -config=Release");
String setCxxBuilderExe = scope .(512);
Path.GetAbsolutePath("../CxxBuilder/dist/CxxBuilder", Directory.GetCurrentDirectory(..scope .(128)), setCxxBuilderExe);
#if BF_PLATFORM_WINDOWS
setCxxBuilderExe.Append(".exe");
#endif
File.WriteAllText("../CxxBuilder/dist/CxxBuilderPath.txt", setCxxBuilderExe);
return 0;
}
}

View File

@@ -157,14 +157,14 @@ static class Program
StreamWriter includeAll = scope .()..Create("clang-c.h"); StreamWriter includeAll = scope .()..Create("clang-c.h");
for (let file in clangFiles) for (let file in clangFiles)
{ {
//if (system(scope $"curl -o {clangHeaders}/{file}.h https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang/include/clang-c/{file}.h") != 0) if (system(scope $"wget -O {clangHeaders}/{file}.h https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang/include/clang-c/{file}.h") != 0)
// Runtime.FatalError(scope $"Failed to download clang-c/{file}"); Runtime.FatalError(scope $"Failed to download clang-c/{file}");
includeAll.Write($"#include <clang-c/{file}.h>\n"); includeAll.Write($"#include <clang-c/{file}.h>\n");
} }
} }
Directory.CreateDirectory("clang-c"); Directory.CreateDirectory("clang-c");
generator.Generate("clang-c.h", null); generator.Generate("clang-c.h");
return 0; return 0;
} }

View File

@@ -64,6 +64,7 @@ abstract class Cpp2BeefGenerator
}, &attrs); }, &attrs);
if (Clang.GetCursorAvailability(cursor) == .Deprecated) str.Append("[Obsolete] "); if (Clang.GetCursorAvailability(cursor) == .Deprecated) str.Append("[Obsolete] ");
//if (Clang.Cursor_IsFunctionInlined(cursor) != 0) str.Append("[Inline] ");
if (cursor.kind == .EnumDecl) str.Append("[AllowDuplicates] "); if (cursor.kind == .EnumDecl) str.Append("[AllowDuplicates] ");
else if (attrs.HasFlag(.Packed)) str.Append("[Packed] "); else if (attrs.HasFlag(.Packed)) str.Append("[Packed] ");
if (attrs.HasFlag(.NoDiscard)) str.Append("[NoDiscard] "); if (attrs.HasFlag(.NoDiscard)) str.Append("[NoDiscard] ");
@@ -154,6 +155,9 @@ abstract class Cpp2BeefGenerator
case .Namespace: BeginCursor(cursor); Namespace(cursor); case .Namespace: BeginCursor(cursor); Namespace(cursor);
case .FunctionDecl: BeginCursor(cursor); FunctionDecl(cursor); case .FunctionDecl: BeginCursor(cursor); FunctionDecl(cursor);
case .FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl:
Runtime.FatalError("Templates are currently unsupported but Coming Soon tm");
case .CXXMethod, case .CXXMethod,
.Constructor, .Constructor,
.Destructor, .Destructor,
@@ -165,6 +169,7 @@ abstract class Cpp2BeefGenerator
case .FieldDecl: BeginCursor(cursor); FieldDecl(cursor); case .FieldDecl: BeginCursor(cursor); FieldDecl(cursor);
case .VarDecl: BeginCursor(cursor); VarDecl(cursor); case .VarDecl: BeginCursor(cursor); VarDecl(cursor);
case .EnumConstantDecl: BeginCursor(cursor); EnumConstantDecl(cursor);
case .MacroDefinition: case .MacroDefinition:
if (Clang.Cursor_IsMacroFunctionLike(cursor) != 0) return; if (Clang.Cursor_IsMacroFunctionLike(cursor) != 0) return;
@@ -194,13 +199,10 @@ abstract class Cpp2BeefGenerator
return false; return false;
} }
protected virtual void ModifyWrapperPrintingPolicy(CXPrintingPolicy policy) {}
protected CXIndex index = Clang.CreateIndex(excludeDeclarationsFromPCH: 0, displayDiagnostics: 1) ~ Clang.DisposeIndex(_); protected CXIndex index = Clang.CreateIndex(excludeDeclarationsFromPCH: 0, displayDiagnostics: 1) ~ Clang.DisposeIndex(_);
protected CXTranslationUnit unit; protected CXTranslationUnit unit;
protected append String str = .(1024); protected append String str = .(1024);
protected append String wrapperBuf = .(1024);
protected CXCursor currentCursor = Clang.GetNullCursor(); protected CXCursor currentCursor = Clang.GetNullCursor();
protected append String cursorIndent = .(16); protected append String cursorIndent = .(16);
@@ -210,8 +212,6 @@ abstract class Cpp2BeefGenerator
protected CXPrintingPolicy printingPolicy; protected CXPrintingPolicy printingPolicy;
private StreamWriter currentWritter; private StreamWriter currentWritter;
private StreamWriter wrapperWritter;
protected StringView WrapperFilePath;
private append String wrapperTemplateChain = .(16); private append String wrapperTemplateChain = .(16);
private append String templateParams = .(16); private append String templateParams = .(16);
@@ -226,7 +226,7 @@ abstract class Cpp2BeefGenerator
protected class FileInfo : this(CXSourceLocation prevEnd, CXFile file) protected class FileInfo : this(CXSourceLocation prevEnd, CXFile file)
{ {
public append String block = .(64); public append String block = .(64);
public enum { None = 0, LSquirly = 1, RSquirly = 0b10 } queuedTokens = .None; public enum { None = 0, LSquirly = 1, RSquirly = _<<1, Semicolon = _<<1 } queuedTokens = .None;
public (CXType type, int32 curWidth) bitfield = default; public (CXType type, int32 curWidth) bitfield = default;
} }
protected FileInfo fileInfo = null; protected FileInfo fileInfo = null;
@@ -276,37 +276,12 @@ abstract class Cpp2BeefGenerator
ParsingFailed ParsingFailed
} }
public Result<void, GenerationError> Generate(char8* headerPath, StringView wrapperPath) public Result<void, GenerationError> Generate(char8* headerPath)
{ {
unitMacros.Clear(); unitMacros.Clear();
fileInfos.Clear(); fileInfos.Clear();
lastRecordOrEnum = Clang.GetNullCursor(); lastRecordOrEnum = Clang.GetNullCursor();
wrapperWritter = scope .();
if (!wrapperPath.IsNull)
wrapperWritter.Create(wrapperPath);
else
wrapperWritter = null;
WrapperFilePath = wrapperPath;
wrapperBuf.Set("""
#define private public
#define protected public
#include <
""");
wrapperBuf.Append(headerPath);
wrapperBuf.Append("""
>
#include <stdint.h>
template <typename T>
using __type = T;
extern "C"
{
""");
let args = Args; let args = Args;
#if DEBUG #if DEBUG
findLang: do findLang: do
@@ -325,7 +300,7 @@ abstract class Cpp2BeefGenerator
if (unit == default) return .Err(.ParsingFailed); if (unit == default) return .Err(.ParsingFailed);
printingPolicy = Clang.GetCursorPrintingPolicy(Clang.GetTranslationUnitCursor(unit)); printingPolicy = Clang.GetCursorPrintingPolicy(Clang.GetTranslationUnitCursor(unit));
ModifyWrapperPrintingPolicy(printingPolicy); Clang.PrintingPolicy_SetProperty(printingPolicy, .TerseOutput, 1);
defer Clang.PrintingPolicy_Dispose(printingPolicy); defer Clang.PrintingPolicy_Dispose(printingPolicy);
PreGeneration(); PreGeneration();
@@ -365,9 +340,6 @@ abstract class Cpp2BeefGenerator
}, Internal.UnsafeCastToPtr(this)); }, Internal.UnsafeCastToPtr(this));
PostGeneration(); PostGeneration();
if (wrapperBuf.IsEmpty)
wrapperWritter.Write("}\n\n//begin-comptime\n");
for (let kv in fileInfos) for (let kv in fileInfos)
{ {
fileInfo = kv.value; fileInfo = kv.value;
@@ -408,7 +380,6 @@ abstract class Cpp2BeefGenerator
switch (cursor.kind) switch (cursor.kind)
{ {
case .FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl: case .FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl:
Runtime.FatalError("Templates are currently unsupported but Coming Soon tm");
templateParams.Append('<'); templateParams.Append('<');
let len = wrapperTemplateChain.Length; let len = wrapperTemplateChain.Length;
Clang.VisitChildren(cursor, (cursor, parent, client_data) => Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
@@ -457,11 +428,6 @@ abstract class Cpp2BeefGenerator
currentWritter.Write(str); currentWritter.Write(str);
str.Clear(); str.Clear();
} }
protected void FlushWrapper()
{
wrapperWritter.Write(wrapperBuf);
wrapperBuf.Clear();
}
protected virtual void GetIndentation(CXSourceLocation location, String outString) protected virtual void GetIndentation(CXSourceLocation location, String outString)
{ {
@@ -479,7 +445,8 @@ abstract class Cpp2BeefGenerator
for (; i < file.Length && file[i].IsWhiteSpace; i++) outString.Append(file[i]); for (; i < file.Length && file[i].IsWhiteSpace; i++) outString.Append(file[i]);
} }
protected virtual void AllWhiteSpaceUntil(CXSourceLocation from, enum { NoNewLines, YesNewLines } newlineMode) protected enum WhiteSpaceNewLineMode { NoNewLines, YesNewLines }
protected virtual void AllWhiteSpaceUntil(CXSourceLocation from, WhiteSpaceNewLineMode newlineMode)
{ {
StringView file; StringView file;
uint32 offset; uint32 offset;
@@ -542,6 +509,9 @@ abstract class Cpp2BeefGenerator
} }
protected virtual void WriteComments(CXSourceLocation writeUntil, Block? block = null) protected virtual void WriteComments(CXSourceLocation writeUntil, Block? block = null)
{ {
var block;
if (fileInfo.queuedTokens != .None) block = null;
Clang.GetFileLocation(writeUntil, let curFile, let curLine, let curColumn, let curOffset); Clang.GetFileLocation(writeUntil, let curFile, let curLine, let curColumn, let curOffset);
Clang.GetFileLocation(fileInfo.prevEnd, let prevFile, let prevLine, let prevColumn, let prevOffset); Clang.GetFileLocation(fileInfo.prevEnd, let prevFile, let prevLine, let prevColumn, let prevOffset);
@@ -587,6 +557,7 @@ abstract class Cpp2BeefGenerator
void Block() void Block()
{ {
if (fileInfo.queuedTokens != .None) return;
findMacros: do findMacros: do
{ {
for (uint32 ln = line; ln < curLine; ln++) for (uint32 ln = line; ln < curLine; ln++)
@@ -618,8 +589,11 @@ abstract class Cpp2BeefGenerator
QueuedToken!(decltype(fileInfo.queuedTokens).LSquirly, "{"); QueuedToken!(decltype(fileInfo.queuedTokens).LSquirly, "{");
QueuedToken!(decltype(fileInfo.queuedTokens).RSquirly, "}"); QueuedToken!(decltype(fileInfo.queuedTokens).RSquirly, "}");
if (!fileInfo.queuedTokens.HasFlag(.RSquirly))
QueuedToken!(decltype(fileInfo.queuedTokens).Semicolon, ";");
if (!isWritingQueuedToken) continue; if (!isWritingQueuedToken) continue;
if (fileInfo.queuedTokens == .None) block = @block;
default: continue; default: continue;
} }
let location = Clang.GetTokenLocation(unit, token); let location = Clang.GetTokenLocation(unit, token);
@@ -637,7 +611,7 @@ abstract class Cpp2BeefGenerator
Flush(); Flush();
} }
WriteLinesUntil!(curLine, writeUntil); WriteLinesUntil!(curLine, writeUntil);
if (block.HasValue) WriteBlock(block.Value); if (@block.HasValue) WriteBlock(@block.Value);
} }
protected enum TypeParamMode { None, InParam, OutParam } protected enum TypeParamMode { None, InParam, OutParam }
@@ -807,7 +781,7 @@ abstract class Cpp2BeefGenerator
str.Append(')'); str.Append(')');
} }
protected void AccessSpecifier(CXCursor cursor) protected virtual void AccessSpecifier(CXCursor cursor)
{ {
switch (Clang.GetCXXAccessSpecifier(cursor)) switch (Clang.GetCXXAccessSpecifier(cursor))
{ {
@@ -818,6 +792,11 @@ abstract class Cpp2BeefGenerator
} }
} }
protected virtual bool IsEmptyStructOpaque(CXCursor cursor)
{
return true;
}
protected mixin BeginBody(CXCursor cursor) protected mixin BeginBody(CXCursor cursor)
{ {
fileInfo.prevEnd = GetCursorAnchor(cursor); fileInfo.prevEnd = GetCursorAnchor(cursor);
@@ -845,6 +824,8 @@ abstract class Cpp2BeefGenerator
switch (cursor.kind) switch (cursor.kind)
{ {
case .StructDecl, .UnionDecl, .ClassDecl: case .StructDecl, .UnionDecl, .ClassDecl:
if (!IsEmptyStructOpaque(cursor))
fallthrough;
str.Append(';'); str.Append(';');
default: default:
str.Append(" {}"); str.Append(" {}");
@@ -856,22 +837,18 @@ abstract class Cpp2BeefGenerator
} }
} }
const int int_maxDigits = scope $"{int.MaxValue:X}".Length + 1;
protected virtual void CppWrapperName(CXCursor cursor, String outString)
{
String hashCode = ScopeCXString!(Clang.GetCursorUSR(cursor))
.GetHashCode().ToString(..scope .(int_maxDigits), "X", null);
outString.Append("cpp2beef_");
if (hashCode.StartsWith('-')) hashCode[0] = 'm';
outString.Append('0', int_maxDigits - hashCode.Length);
outString.Append(hashCode);
}
protected virtual enum { C, Cpp } Linkable_Attributes(CXCursor cursor) protected virtual enum { C, Cpp } Linkable_Attributes(CXCursor cursor)
{ {
let mangledName = ScopeCXString!(Clang.Cursor_GetMangling(cursor)); let mangledName = ScopeCXString!(Clang.Cursor_GetMangling(cursor));
let name = GetNameInBindings(cursor, ..scope .(mangledName.Length)); let name = GetNameInBindings(cursor, ..scope .(mangledName.Length));
WriteCustomAttributes(cursor); WriteCustomAttributes(cursor);
void LinkNameWhitespace()
{
if (Flags.HasFlag(.PreseveColumns))
str..Append('\n')..Append(cursorIndent);
else
str.Append(' ');
}
if (mangledName == name) if (mangledName == name)
{ {
str.Append("[CLink] "); str.Append("[CLink] ");
@@ -881,20 +858,16 @@ abstract class Cpp2BeefGenerator
{ {
str.Append("[LinkName("); str.Append("[LinkName(");
mangledName.QuoteString(str); mangledName.QuoteString(str);
str.Append(")] "); str.Append(")]");
LinkNameWhitespace();
return .C; return .C;
} }
else else
{ {
str.Append("[LinkName(\""); str.Append("[LinkName(");
CppWrapperName(cursor, str); ScopeCXString!(Clang.GetCursorPrettyPrinted(cursor, printingPolicy)).QuoteString(str);
str.Append('"'); str.Append(")]");
if (!wrapperTemplateChain.IsEmpty) LinkNameWhitespace();
if (templateParams.IsEmpty)
str.Append(" + __template_chain");
else
str.Append(" + CppWrapperF($\"", wrapperTemplateChain, "\")");
str.Append(")] ");
return .Cpp; return .Cpp;
} }
} }
@@ -923,7 +896,8 @@ abstract class Cpp2BeefGenerator
currentCursor = cursor; currentCursor = cursor;
} }
protected virtual void WriteTypeAndName(CXCursor cursor, CXType type, enum { Standard, TypeAlias, ConversionFunction, Ctor, Dtor } format = .Standard) protected enum TypeAndNameKind { Standard, TypeAlias, ConversionFunction, Ctor, Dtor }
protected virtual void WriteTypeAndName(CXCursor cursor, CXType type, TypeAndNameKind format = .Standard)
{ {
bool preserveColumns = Flags.HasFlag(.PreseveColumns); bool preserveColumns = Flags.HasFlag(.PreseveColumns);
Flush(); Flush();
@@ -940,7 +914,7 @@ abstract class Cpp2BeefGenerator
String whitespace; String whitespace;
if (preserveColumns) if (preserveColumns)
{ {
whitespace = scope .(str); whitespace = scope:: .(str);
str.Clear(); str.Clear();
Type(type); Type(type);
whitespace.Length -= Math.Min(str.Length, whitespace.Length); whitespace.Length -= Math.Min(str.Length, whitespace.Length);
@@ -1101,89 +1075,9 @@ abstract class Cpp2BeefGenerator
} }
} }
protected virtual void WriteMethodWrapper(CXCursor cursor)
{
let parent = Clang.GetCursorType(Clang.GetCursorSemanticParent(cursor));
var resultType = Clang.GetCursorResultType(cursor);
bool nonStatic = (cursor.kind == .CXXMethod && Clang.CXXMethod_IsStatic(cursor) == 0) || cursor.kind == .Destructor || cursor.kind == .ConversionFunction;
if (!wrapperTemplateChain.IsEmpty)
FlushWrapper();
StringView parentSpelling = "\" + __cpp_type + \"";
if (wrapperTemplateChain.IsEmpty)
parentSpelling = ScopeCXString!::(Clang.GetFullyQualifiedName(parent, printingPolicy, 0));
if (cursor.kind == .Constructor)
wrapperBuf.Append("__type<", parentSpelling, "> ");
else
{
wrapperBuf.Append("__type<");
wrapperBuf.Append(ScopeCXString!(Clang.GetFullyQualifiedName(resultType, printingPolicy, 0)));
wrapperBuf.Append("> ");
}
CppWrapperName(cursor, wrapperBuf);
if (!wrapperTemplateChain.IsEmpty)
{
if (templateParams.IsEmpty)
wrapperBuf.Append("\" + __template_chain + \"");
else
wrapperBuf.Append("\" + CppWrapperF($\"", wrapperTemplateChain, "\") + \"");
}
wrapperBuf.Append('(');
if (nonStatic)
wrapperBuf.Append(parentSpelling, " *self");
for (int i < Clang.Cursor_GetNumArguments(cursor))
{
let arg = Clang.Cursor_GetArgument(cursor, (.)i);
if (nonStatic || i > 0) wrapperBuf.Append(", ");
wrapperBuf.Append("__type<");
wrapperBuf.Append(ScopeCXString!(Clang.GetFullyQualifiedName(Clang.GetCursorType(arg), printingPolicy, 0)));
wrapperBuf.Append("> p");
i.ToString(wrapperBuf);
}
wrapperBuf.Append(") { ");
if (resultType.kind != .Void) wrapperBuf.Append("return ");
if (nonStatic)
{
if (cursor.kind != .Destructor)
wrapperBuf.Append("self->", GetCursorSpelling!(cursor));
else
wrapperBuf.Append("self->~", parentSpelling);
}
else if (cursor.kind == .Constructor)
wrapperBuf.Append(parentSpelling);
else
wrapperBuf.Append(fullCursorName);
wrapperBuf.Append('(');
for (int i < Clang.Cursor_GetNumArguments(cursor))
{
if (i > 0) wrapperBuf.Append(", ");
wrapperBuf.Append('p');
i.ToString(wrapperBuf);
}
wrapperBuf.Append("); }");
if (wrapperTemplateChain.IsEmpty)
{
wrapperBuf.Append('\n');
FlushWrapper();
}
else
{
if (defferedWrapperWrite == null)
defferedWrapperWrite = new .(1024);
defferedWrapperWrite.Append(cursorIndent, "\t + \"extern \\\"C\\\" ");
wrapperBuf.ToString(defferedWrapperWrite);
defferedWrapperWrite.Append("\\n\"\n");
wrapperBuf.Clear();
}
}
protected virtual void FunctionDecl(CXCursor cursor) protected virtual void FunctionDecl(CXCursor cursor)
{ {
if (Linkable_Attributes(cursor) == .Cpp) Linkable_Attributes(cursor);
WriteMethodWrapper(cursor);
AccessSpecifier(cursor); AccessSpecifier(cursor);
str.Append("static extern "); str.Append("static extern ");
WriteTypeAndName(cursor, Clang.GetCursorResultType(cursor)); WriteTypeAndName(cursor, Clang.GetCursorResultType(cursor));
@@ -1198,8 +1092,7 @@ abstract class Cpp2BeefGenerator
{ {
void Attributes() void Attributes()
{ {
if (Linkable_Attributes(cursor) == .Cpp) Linkable_Attributes(cursor);
WriteMethodWrapper(cursor);
} }
let spelling = GetCursorSpelling!(cursor); let spelling = GetCursorSpelling!(cursor);
@@ -1335,18 +1228,6 @@ abstract class Cpp2BeefGenerator
if (linkLang == .Cpp && type.kind != .LValueReference && type.kind != .RValueReference) if (linkLang == .Cpp && type.kind != .LValueReference && type.kind != .RValueReference)
str.Append("ref "); str.Append("ref ");
WriteTypeAndName(cursor, type); WriteTypeAndName(cursor, type);
if (linkLang == .Cpp)
{
let wrapperName = CppWrapperName(cursor, ..scope .());
str.Append(" { [LinkName(\"", wrapperName, "\")] get; }");
wrapperBuf.Append("__type<");
wrapperBuf.Append(ScopeCXString!(Clang.GetFullyQualifiedName(type, printingPolicy, 0)));
wrapperBuf.Append("> ");
if (type.kind != .LValueReference && type.kind != .RValueReference)
wrapperBuf.Append('&');
wrapperBuf.Append(wrapperName, "() { return ", fullCursorName, "; }\n");
FlushWrapper();
}
str.Append(';'); str.Append(';');
default: default:
Runtime.FatalError(scope $"Unhandled var linkage: {_}"); Runtime.FatalError(scope $"Unhandled var linkage: {_}");
@@ -1371,15 +1252,16 @@ abstract class Cpp2BeefGenerator
}, null) != 0; }, null) != 0;
} }
protected virtual void Record(CXCursor cursor) protected virtual void Record(CXCursor cursor, bool attributes = true)
{ {
WriteCustomAttributes(cursor); WriteCustomAttributes(cursor);
switch (cursor.kind) if (attributes)
{ switch (cursor.kind)
case .StructDecl, .ClassDecl: str.Append("[CRepr] "); {
case .UnionDecl: str.Append("[CRepr, Union] "); case .StructDecl, .ClassDecl: str.Append("[CRepr] ");
default: Runtime.FatalError("Unhandled record type"); case .UnionDecl: str.Append("[CRepr, Union] ");
} default: Runtime.FatalError("Unhandled record type");
}
AccessSpecifier(cursor); AccessSpecifier(cursor);
str.Append("struct "); str.Append("struct ");
if (Clang.Cursor_IsAnonymous(cursor) == 0) if (Clang.Cursor_IsAnonymous(cursor) == 0)
@@ -1473,9 +1355,22 @@ abstract class Cpp2BeefGenerator
Self self = (.)Internal.UnsafeCastToObject(client_data); Self self = (.)Internal.UnsafeCastToObject(client_data);
if (self.fileInfo.bitfield != default && Clang.Cursor_IsBitField(cursor) == 0) if (self.fileInfo.bitfield != default && Clang.Cursor_IsBitField(cursor) == 0)
self.DumpBitfieldStorage(); self.DumpBitfieldStorage();
self.WriteCursor(cursor);
if (Clang.Cursor_IsAnonymousRecordDecl(cursor) != 0) if (Clang.Cursor_IsAnonymousRecordDecl(cursor) != 0)
self.str..TrimEnd()..Append(";"); {
self.BeginCursor(cursor);
switch (cursor.kind)
{
case .StructDecl, .ClassDecl: self.str.Append("[CRepr] ");
case .UnionDecl: self.str.Append("[CRepr, Union] ");
default: Runtime.FatalError("Unhandled record type");
}
self.AccessSpecifier(cursor);
self.str.Append("using ");
self.Record(cursor, attributes: false);
self.fileInfo.queuedTokens |= .Semicolon;
}
else
self.WriteCursor(cursor);
return .Continue; return .Continue;
}, Internal.UnsafeCastToPtr(this)); }, Internal.UnsafeCastToPtr(this));
@@ -1507,18 +1402,21 @@ abstract class Cpp2BeefGenerator
{ {
Self self = (.)Internal.UnsafeCastToObject(client_data); Self self = (.)Internal.UnsafeCastToObject(client_data);
Runtime.Assert(cursor.kind == .EnumConstantDecl); Runtime.Assert(cursor.kind == .EnumConstantDecl);
self.HandleCursor(cursor);
self.BeginCursor(cursor);
self.WriteCustomAttributes(cursor);
self.GetNameInBindings(cursor, self.str);
self.WriteTokensAfterEquals(cursor);
//Clang.GetEnumConstantDeclValue(cursor).ToString(self.str);
self.str.Append(',');
return .Continue; return .Continue;
}, Internal.UnsafeCastToPtr(this)); }, Internal.UnsafeCastToPtr(this));
} }
protected virtual void EnumConstantDecl(CXCursor cursor)
{
BeginCursor(cursor);
WriteCustomAttributes(cursor);
GetNameInBindings(cursor, str);
WriteTokensAfterEquals(cursor);
//Clang.GetEnumConstantDeclValue(cursor).ToString(str);
str.Append(',');
}
protected virtual void TypeAlias(CXCursor cursor) protected virtual void TypeAlias(CXCursor cursor)
{ {
CXType type; CXType type;