Giter Club home page Giter Club logo

c2cs's Introduction

C2CS

C to C# library bindings code generator. In go .h file, out come .cs file.

Documentation

For documentation on supported platforms, limitations, how to install C2CS, how to use C2CS, how to build C2CS, etc, see the docs/README.md.

License

C2CS is licensed under the MIT License (MIT).

There are a few exceptions to this detailed below. See the LICENSE file for more details on this main product's license.

c2cs's People

Contributors

anggape avatar jakemcf22 avatar joelmartinez avatar lithiumtoast avatar waldnercharles avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

c2cs's Issues

Zig

TODO:

  • Add minimal sample of using Zig library from C#.

BOCKED BY:

Header files are not excluded when they do not declare any types

If a file is included in excludedHeaderFiles but doesn't declare any types or functions, it will not be excluded. This is a problem when the header file is including other header files that do declare types.

A good example of this is windows.h. It doesn't declare any types itself, but includes quite a few headers that will be visited during ExploreTranslationUnit.

An easy way to replicate the issue is to do the following:

// Library.h
#include "ParentHeader.h"

// ParentHeader.h
#include "ChildHeader.h"

// ChildHeader.h
struct MyStruct
{
    int x;
    int y;
}

// config.json
{
  "inputFilePath": "Library.h",
  "outputFilePath": "Library.cs",
  "excludedHeaderFiles": ["ParentHeader.h"]
}

In the example above, MyStruct will be generated in Library.cs.

Alternatively on windows, just try to #include <windows.h> and "excludeHeaderFiles": ["windows.h"]

Look into limited C++ types

Use case: https://partner.steamgames.com/doc/sdk

Steamworks SDK provides a C header for exposing function names; all good. Problem is that the transitive types used in functions have C++.

libclang: Parsing './ext/steam/steam_api_flat.h' with the following arguments...
        --language=c --std=c11 -Wno-pragma-once-outside-header -fno-blocks -m64 --include-directory=/Users/lstranks/Programming/steamworks-cs/ext --define-macro=STEAM_API_EXPORTS -isystem/Library/Developer/CommandLineTools/usr/lib/clang/12.0.5/include -isystem/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/usr/include
Clang diagnostics:
        ./ext/steam/steamtypes.h:132:2: error: must use 'enum' tag to refer to type 'ESteamIPType'
        ./ext/steam/steamtypes.h:134:2: error: unknown type name 'bool'
        ./ext/steam/steamtypes.h:134:14: error: expected ';' at end of declaration list
        ./ext/steam/steamclientpublic.h:460:1: error: unknown type name 'class'
        ./ext/steam/steamclientpublic.h:460:15: error: expected ';' after top level declarator
        ./ext/steam/steamclientpublic.h:836:8: error: unknown type name 'bool'
        ./ext/steam/steamclientpublic.h:836:1: error: 'inline' can only appear on functions
        ./ext/steam/steamclientpublic.h:836:21: error: expected ';' after top level declarator
        ./ext/steam/steamclientpublic.h:836:22: error: expected identifier or '('
        ./ext/steam/steamclientpublic.h:899:1: error: unknown type name 'class'
        ./ext/steam/steamclientpublic.h:899:14: error: expected ';' after top level declarator
        ./ext/steam/steam_api_common.h:58:8: error: expected identifier or '('
        ./ext/steam/steam_api_common.h:59:8: error: expected identifier or '('
        ./ext/steam/steam_api_common.h:95:1: error: expected identifier or '('
        ./ext/steam/steam_api_common.h:112:1: error: expected identifier or '('
        ./ext/steam/steam_api_common.h:124:1: error: unknown type name 'class'
        ./ext/steam/steam_api_common.h:124:20: error: expected ';' after top level declarator
        ./ext/steam/steam_api_common.h:148:1: error: unknown type name 'template'
        ./ext/steam/steam_api_common.h:148:9: error: expected identifier or '('
        fatal error: too many errors emitted, stopping now [-ferror-limit=]

An example:

struct SteamIPAddress_t
{
	union {

		uint32			m_unIPv4;		// Host order
		uint8			m_rgubIPv6[16];		// Network order! Same as inaddr_in6.  (0011:2233:4455:6677:8899:aabb:ccdd:eeff)

		// Internal use only
		uint64			m_ipv6Qword[2];	// big endian
	};

	ESteamIPType m_eType;

	bool IsSet() const 
	{ 
		if ( k_ESteamIPTypeIPv4 == m_eType )
		{
			return m_unIPv4 != 0;
		}
		else 
		{
			return m_ipv6Qword[0] !=0 || m_ipv6Qword[1] != 0; 
		}
	}

	static SteamIPAddress_t IPv4Any()
	{
		SteamIPAddress_t ipOut;
		ipOut.m_eType = k_ESteamIPTypeIPv4;
		ipOut.m_unIPv4 = 0;

		return ipOut;
	}

	static SteamIPAddress_t IPv6Any()
	{
		SteamIPAddress_t ipOut;
		ipOut.m_eType = k_ESteamIPTypeIPv6;
		ipOut.m_ipv6Qword[0] = 0;
		ipOut.m_ipv6Qword[1] = 0;

		return ipOut;
	}

	static SteamIPAddress_t IPv4Loopback()
	{
		SteamIPAddress_t ipOut;
		ipOut.m_eType = k_ESteamIPTypeIPv4;
		ipOut.m_unIPv4 = 0x7f000001;

		return ipOut;
	}

	static SteamIPAddress_t IPv6Loopback()
	{
		SteamIPAddress_t ipOut;
		ipOut.m_eType = k_ESteamIPTypeIPv6;
		ipOut.m_ipv6Qword[0] = 0;
		ipOut.m_ipv6Qword[1] = 0;
		ipOut.m_rgubIPv6[15] = 1;

		return ipOut;
	}
};

Macros

https://github.com/bkaradzic/bgfx/blob/master/include/bgfx/defines.h#L37

This is a show stopper to create bindings for bgfx.

The constants could easily be converted to enums based on analyzing the prefix of macros and checking their value type.

However, the macros with one or more arguments like https://github.com/bkaradzic/bgfx/blob/master/include/bgfx/defines.h#L535 become troublesome...

First intuition is to convert these type of macros to C# static methods with an inline attribute. But what then? The param types can't be known in advance which would be required for a C# static method... What's worse is that there is no guarantee that these macros are actually part of the public C API. This would mean that any and all macros would need to be transpiled to C# regardless if they are or are not part of the public C API...

I think perhaps the problem is macros in general. It's sad that bgfx is using macros.

If anyone has any better idea how to solve I'm all ears.

NuGet tool

TODO:

  • Setup workflow automation for building and deploying C2CS command line program as NuGet tool; triggered by new release tag
  • Update README with how to get/use C2CS using NuGet tool

Sanitize unnamed const params

It seems like unnamed const pointers aren't being sanitized correctly for function pointers.

The issue is with the 2nd parameter: HRESULT (IRpcStubBuffer *, IID *const, void **)

IID and IID* exist in the types dictionary, but, TypeNameMapFunctionPointer is looking for IID*const.

Possible test cases:

const int *ptr;
const int* ptr;
int const *ptr;
int const* ptr;
const int const *ptr;
const int const* ptr;

Automate bindings for Objective C

Look into /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk/usr/include/objc/NSObject.h and see if libclang can parse it. If yes, then next is can C2CS leverage libclang to some how generate C# bindings for ObjectiveC code.

Use pseudo enums

Problem is that if a enum is in the external linkage but it is not transitive to a function, then it may be used in the API but in C an enum can be freely cast to a int or unsigned int without loss.

E.g. in C for the enum SDL_WindowFlags:

/**
 * Create a window and default renderer.
 *
 * \param width the width of the window
 * \param height the height of the window
 * \param window_flags the flags used to create the window (see
 *                     SDL_CreateWindow())
 * \param window a pointer filled with the window, or NULL on error
 * \param renderer a pointer filled with the renderer, or NULL on error
 * \returns 0 on success, or -1 on error; call SDL_GetError() for more
 *          information.
 *
 * \sa SDL_CreateRenderer
 * \sa SDL_CreateWindow
 */
extern DECLSPEC int SDLCALL SDL_CreateWindowAndRenderer(
                                int width, int height, Uint32 window_flags,
                                SDL_Window **window, SDL_Renderer **renderer);

...

/**
 *  \brief The flags on a window
 *
 *  \sa SDL_GetWindowFlags()
 */
typedef enum
{
    SDL_WINDOW_FULLSCREEN = 0x00000001,         /**< fullscreen window */
    SDL_WINDOW_OPENGL = 0x00000002,             /**< window usable with OpenGL context */
    SDL_WINDOW_SHOWN = 0x00000004,              /**< window is visible */
    SDL_WINDOW_HIDDEN = 0x00000008,             /**< window is not visible */
    SDL_WINDOW_BORDERLESS = 0x00000010,         /**< no window decoration */
    SDL_WINDOW_RESIZABLE = 0x00000020,          /**< window can be resized */
    SDL_WINDOW_MINIMIZED = 0x00000040,          /**< window is minimized */
    SDL_WINDOW_MAXIMIZED = 0x00000080,          /**< window is maximized */
    SDL_WINDOW_MOUSE_GRABBED = 0x00000100,      /**< window has grabbed mouse input */
    SDL_WINDOW_INPUT_FOCUS = 0x00000200,        /**< window has input focus */
    SDL_WINDOW_MOUSE_FOCUS = 0x00000400,        /**< window has mouse focus */
    SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
    SDL_WINDOW_FOREIGN = 0x00000800,            /**< window not created by SDL */
    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported.
                                                     On macOS NSHighResolutionCapable must be set true in the
                                                     application's Info.plist for this to have any effect. */
    SDL_WINDOW_MOUSE_CAPTURE    = 0x00004000,   /**< window has mouse captured (unrelated to MOUSE_GRABBED) */
    SDL_WINDOW_ALWAYS_ON_TOP    = 0x00008000,   /**< window should always be above others */
    SDL_WINDOW_SKIP_TASKBAR     = 0x00010000,   /**< window should not be added to the taskbar */
    SDL_WINDOW_UTILITY          = 0x00020000,   /**< window should be treated as a utility window */
    SDL_WINDOW_TOOLTIP          = 0x00040000,   /**< window should be treated as a tooltip */
    SDL_WINDOW_POPUP_MENU       = 0x00080000,   /**< window should be treated as a popup menu */
    SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000,   /**< window has grabbed keyboard input */
    SDL_WINDOW_VULKAN           = 0x10000000,   /**< window usable for Vulkan surface */
    SDL_WINDOW_METAL            = 0x20000000,   /**< window usable for Metal view */

    SDL_WINDOW_INPUT_GRABBED = SDL_WINDOW_MOUSE_GRABBED /**< equivalent to SDL_WINDOW_MOUSE_GRABBED for compatibility */
} SDL_WindowFlags;

...

SDL_CreateWindow(
                "SDL2: Hello, world!",
                100,
                100,
                800,
                600,
                SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

For the case where an enum is not directly used as a type in a function and is not used anywhere elsewhere (it is not transitive), then in C# the enum values should just be constants instead.

E.g. in C#:

public static class SDL
{
    public const uint 
              SDL_WINDOW_FULLSCREEN = 0x1U,
              SDL_WINDOW_OPENGL = 0x2U,
              SDL_WINDOW_SHOWN = 0x4U,
              ...
};

`wchar_t`

Problem:

  • Windows, wchar_t is 2 bytes by default.
  • Linux, wchar_t is 4 bytes by default.

This can be configured by the compiler to force wchar_t to be 2 or 4 bytes using the -fshort-wchar flag. However, this has extreme consequences. Hardware vendors warn that all linked objects must use the same wchar_t size, including libraries. It is then not possible or at the very least unstable to link an object file compiled with -fshort-wchar, with another object file that is compiled without -fshort-wchar. It is not clear what happens when dynamic loading a library, but for NativeAOT dynamic linking is a real thing for C#.

This makes wchar_t by default not a good scenario for single-source cross-platform bindings.
What's worse is that in C# strings are UTF-16 where char is 2 bytes. This means that some marshalling either by hand or otherwise has to be done to get correct behaviour for passing wchar_t strings between C and C# on Linux.

Microsoft has a discussion of introducing a UTF8String, but this would only be helpful for dealing with the interoperability of char* not wchar_t*.

Options:

  1. Enforce the use of -fshort-wchar compiler flag for all users of C2CS so that wchar_t is guaranteed to be 2 bytes. This has the consequence that users will need to re-compile their C code to be compliant.
  2. Use by hand marshalling or an ICustomMarshaler with different implementations for Windows and Linux so that one C# .cs file for bindings can be used correctly for Windows and Linux when passing wchar_t* between C# and C.
  3. Warn users of C2CS that usage of wchar_t* falls into the same category as pointers and thus different .cs files of bindings will need to be generated for each ABI. For example, a different .cs file would need to be generated for Windows and Linux where wchar_t usage is correct.

Not external function

The function below was mistakenly transpiled; but it happens only on Windows?

/**
 * Returns true if point resides inside a rectangle.
 */
SDL_FORCE_INLINE SDL_bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
{
    return ( (p->x >= r->x) && (p->x < (r->x + r->w)) &&
             (p->y >= r->y) && (p->y < (r->y + r->h)) ) ? SDL_TRUE : SDL_FALSE;
}
        // Function @ SDL_rect.h:110:27
        //	x86_64-pc-windows-msvc
        //	aarch64-pc-windows-msvc
        [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
        public static extern CBool SDL_PointInRect(SDL_Point* p, SDL_Rect* r);

Attempting to use `CBool.ToBoolean()` causes a `StackOverflowException`

For example this code:

testCBool = true;
SDL2.SDL.Runtime.CBool.ToBoolean(testCBool);

Generates this error:

Stack overflow.
Repeat 9630 times:
--------------------------------
   at SDL2.SDL+Runtime+CBool.ToBoolean(CBool)
   at SDL2.SDL+Runtime+CBool.op_Implicit(CBool)
--------------------------------
   at VectorVenture.App.Initialize()
   at Katabasis.Game.DoInitialize()
   at Katabasis.Game.Run()
   at VectorVenture.Program.Main()

I can get around this by rewriting CBool.ToBoolean() like this:

public static bool ToBoolean(CBool value)
{
        //Workaround
        return value == CBool.FromBoolean(true);

        //Original code:
        //return Convert.ToBoolean(value);
}

But I'm assuming that's not identical to the C function.

Struct inheritance

Found when doing bindgen for libuv

struct uv_stream_s {
  UV_HANDLE_FIELDS
  UV_STREAM_FIELDS
};

struct uv_tcp_s {
  UV_HANDLE_FIELDS
  UV_STREAM_FIELDS
  UV_TCP_PRIVATE_FIELDS
};

struct uv_pipe_s {
  UV_HANDLE_FIELDS
  UV_STREAM_FIELDS
  int ipc; /* non-zero if this pipe is used for passing handles */
  UV_PIPE_PRIVATE_FIELDS
};

Would it make sense to have an implicit cast operator as a pointer value for "subclass" structs?

Empty generation

Hello there,

I hope this message finds you well. First of all, thank you for your work! I was able to build the generator and test it out, however I think there is something I miss. The generator generates empty classes i.e sokol_gfx, is there something that I miss?

image

Fields in anonymous union inside a struct is not properly handled as documented.

C definition inside the header file:

struct SomeStruct
{
   // other fields
    union
    {
        struct Foo foo;
        struct Bar bar;
    };
    //other fields
};

The struct is correctly taken as LayoutKind.Explicit, and the field offsets of the normal fields are correctly calculated, but the fields inside the anonymous union is simply ignored.

Fails to build

Error CS0246 The type or namespace name 'GeneratePlatformInvokeCodeUseCase' could not be found (are you missing a using directive or an assembly reference?) C2CS.CommandLine

Error CS0246 The type or namespace name 'CodeCParser' could not be found (are you missing a using directive or an assembly reference?) C2CS.CommandLine

Multi-pass: C Locations

Ensure that C locations are taken from a preferred source if possible. e.g. when using multi-pass with flecs, the locations flip-flop between Windows and Linux file paths.

        // Function @ flecs.h:4543:19 (/Users/runner/work/flecs-cs/flecs-cs/ext/flecs/include/flecs.h)
        [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
        public static extern ecs_type_t* ecs_table_get_type(ecs_table_t* table);
        // FunctionPointer @ system.h:66:16 (D:\a\flecs-cs\flecs-cs\ext\flecs\include\flecs\addons\system.h)
        [StructLayout(LayoutKind.Sequential)]
        public struct ecs_system_status_action_t
        {
            public delegate* unmanaged<ecs_world_t*, ecs_entity_t, ecs_system_status_t, void*, void> Pointer;
        }

Python

Since extracting the abstract syntax tree of a header file is now de-coupled in it's own step using ast it is now possible to think about the possibility of generating code for other languages.

I would think Python would be a good experimentation as a next step.

How to use StringBuilder for char* used as output

I generated bindings for a closed-source C API, that contains many functions of the form

int getSomething(char* input, char *output)

In the generated C# code these functions are mapped to

int getSomething(CString input, CString output)

The problem is, the C code expects output to be a pointer to a buffer of a given size. This can be accomplished by marshalling a StringBuilder.

Is it possible to map char * to StringBuilder and char* to CString? What other solution do you propose?

does it work with wchar_t?

I am getting an error with wchar_t.
I also included #include <stddef.h> to bypass clang error but I do get following

PANIC: [Panic] String cannot be of zero length. (Parameter 'oldValue')
   at System.String.Replace(String oldValue, String newValue)
   at C2CS.UseCases.AbstractSyntaxTreeC.ClangTranslationUnitExplorer.Location(CXCursor cursor, Nullable`1 type)
   at C2CS.UseCases.AbstractSyntaxTreeC.ClangTranslationUnitExplorer.VisitTranslationUnit(CXTranslationUnit translationUnit)
   at C2CS.UseCases.AbstractSyntaxTreeC.ClangTranslationUnitExplorer.AbstractSyntaxTree(CXTranslationUnit translationUnit, Int32 bitness)
   at C2CS.UseCases.AbstractSyntaxTreeC.UseCase.Explore(CXTranslationUnit translationUnit, ImmutableArray`1 includeDirectories, ImmutableArray`1 ignoredFiles, ImmutableArray`1 opaqueTypes, ImmutableArray`1 whitelistFunctionNames, Int32 bitness)
   at C2CS.UseCases.AbstractSyntaxTreeC.UseCase.Execute(Request request, Response response)
   at C2CS.UseCase`2.Execute(TRequest request)

Compatible with `bflat` for statically linking

Bflat is a tool for making use of native AOT more simple.

One of problems with current bindgen with C2CS however is that dlsym/GetProcAddess does not work for statically linked libraries with bflat. The way it works for bflat is that it repurposes DllImport attributes to understand which functions are to be used from a statically linked library.

Thus, the bindgen for C2CS needs to change to use DllImport for extern functions.

Consequences:

  1. Extern variables will be removed from bindgen. DllImport only works for functions not variables even though dlsym and GetProcAddress work for functions and variables. A new diagnostic should be added giving some indication that variables will not be transpiled for bindgen when they are discovered. The only workaround is to add "getter" and "setter" functions in C for the variables which are to be exposed to C#.
  2. No longer be able to unload/reload extern functions. Functions will be "loaded" at first opportunity with shared libraries to be on par with static libraries. The only way to "unload" or "remap" the function externs for shared libraries is to re-start the application.

Add Cdecl calling convention attribute

Technically this is only required for Windows; I don't primarily use Windows. One problem however is that it is an assumption because technically a developer could use cdecl or another calling convention when they write their C library if they so choose to. Is it possible to know the calling convention using clang?

Add regions for grouping different header files contents in the one output `.cs` file

Group C# syntax nodes into regions with the file name of the header file. This increases visibility into navigating the .cs file using an IDE like Rider or plugins to text editors like Visual Studio Code.

e.g.

namespace my_c_library
{
    public static unsafe partial class my_c_library
    {
        private const string LibraryName = "my_c_library";
        
        #region pinvoke_helper.h

        // Function @ pinvoke_helper.h:93:30
        [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
        public static extern CString pinvoke_get_platform_name();
        
        #endregion
        
        #region my_c_library.h
        
        // Function @ my_c_library.h:63:23
        [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void function_void_void();
        
        #endregion
        
        ...
    }
}

Union / struct support

Hello how tricky would it be to implement union support? I faced an issue of not being able to generate bindings for complex header.
image

public StructDeclarationSyntax CreateStruct(CXCursor clangRecord)
		{
			if (clangRecord.IsAnonymous)
			{
				// TODO: Anon structs/unions
				throw new NotImplementedException();
			}

I believe it failed on this part of the header

typedef struct ddsrt_log_cfg {
  struct ddsrt_log_cfg_common c;
  union {
    dds_log_write_fn_t fnptr;
    void *ptr;
    uint32_t u32;
    unsigned char pad[72];
  } u;
} ddsrt_log_cfg_t;

Here is the header in case if you would want to try: https://pastebin.com/Bc78yGg5

Unity compatible function delegates

@lithiumtoast are you open to supporting Unity compatible code generation?

If so I will submit a PR that optionally falls back to legacy function delegates as C#9 function pointers are not fully supported in Unity 2021+ yet. I've already implemented this in my own branch.

Use `libclang` with C++ instead of `Rosyln` to figure out macro types

Currently C2CS uses Rosyln to figure out the type of a macro object. This happens by creating a new adhoc .cs file and evaluating the expression using var and letting Rosyln figure out what is the appropriate type through semantic analysis. I didn't think about it before, but technically the same approach could be done usinglibclang with auto in C++.

This would remove the slowest part of C2CS currently making C2CS run much faster. It would also allow C2CS to not rely on the .NET runtime/sdk being installed in a specific location which opens the path for true single file deployments and NativeAOT.

Do not insert path to header file in resulting .cs

Hi,

Currently the generator is inserting the path to the file above the extern function such as:

    // Function @ sokol_app.h:1412:24 (/home/runner/work/sokol-cs/sokol-cs/ext/sokol/sokol_app.h)
    [DllImport(LibraryName)]
    public static extern int sapp_height();

But I don't want to insert paths specific to my configuration in the git repository (if another developer runs the script, these paths will get overridden with new paths even though that's not significant).
What about a new option to prevent path insertion and get the following instead?

    // Function @ sokol_app.h:1412:24
    [DllImport(LibraryName)]
    public static extern int sapp_height();

Wrap into namespace

Hi,

It appears we can't tell the tool to wrap the resulting class into a namespace such as:

namespace Devolutions.Picky { // <---- here
  //-------------------------------------------------------------------------------------
  // <auto-generated>
  //     This code was generated by the following tool:
  //        https://github.com/lithiumtoast/c2cs (v0.0.0.0)
  //
  //     Changes to this file may cause incorrect behavior and will be lost if
  //     the code is regenerated.
  // </auto-generated>
  // ReSharper disable All
  //-------------------------------------------------------------------------------------
  using System;
  using System.Runtime.InteropServices;
  using System.Runtime.CompilerServices;
  
  using C2CS;
  
  #nullable enable
  #pragma warning disable 1591
  
  public static unsafe partial class Native
  {
      private const string LibraryName = "picky";
  
      // Function @ picky.h:24:6 (/home/…)
      [DllImport(LibraryName)]
      public static extern void picky_clear_last_error();}
}

or

namespace Devolutions.Picky; // <-- prepend

I think a new --namespace argument to the cs subcommand could be useful for that.

Cheers

`va_list`

Found when generating bindings for flecs: https://github.com/SanderMertens/flecs

/* Append format string with argument list to a buffer.
 * Returns false when max is reached, true when there is still space */
FLECS_API
bool ecs_strbuf_vappend(
    ecs_strbuf_t *buffer,
    const char *fmt,
    va_list args);

It's not clear how va_list should be handled.

There is some discussion here on a similar issue: dotnet/runtime#9316

Source generators

Been experimenting with source generators; they do work and are very cool. The benefit of source generators is that it allows C2CS to be a "plugin" to the Rosyln compiler. It would run at build time for each project and add the generated source code to the final compilation. For this project itself, this would be beneficial for building the examples as it would make them smoke tests which can be executed via GitHub actions. It would then be immediately obvious if any the examples are broken.

Problems:

  1. Rider has some visual hiccups with generated code not being displayed (building the project in Rider works fine, but intellisense doesn't work.) I expect JetBrains to iron out these issue over time. Rider uses a completely different engine for C# and thus always lags behind a bit on bleeding edge or fairly new features of C#. For this reason alone, source generators are in my opinion not ready for prime time.

  2. Source generators have to target .netstandard2.0. C2CS uses bindings for libclang with some callbacks to C# from C which the UnmanagedCallersOnly attribute is used. This attribute is not available in .netstandard2.0, it was introduced in .net5.0. Callbacks to C# from C have to fallback to using Marshal.GetFunctionPointerForDelegate to which completely eliminates the benefit of using C# function pointers. This is the exact problem for #67 to which this is not a deal breaker for C2CS to use source generators per se from a technical perspective. However it is annoying; I rather keep C2CS on the train of .NET Core/5/6/7 because it gives me an excuse to learn and play with the latest iterations of .NET. The work around I have been doing for now is to execute C2CS from command line as a .net6.0 application from a .netstandard2.0 source generator context. Which goes into the next problem...

  3. Source generators are ripe for opening of the possibility of malware/spyware/etc. Because source generators are essentially a plugin to the Rosyln compiler, a developer would be running some C# code at build time. When you open up a project in Rider for the first time it asks you "do you trust this project?". That message totally makes sense to me now because anyone could clone down a Git repository of some C# code and open it up with Rider to which building the project could be executing code you may or may not trust completely. Since I execute the .net6.0 version of C2CS by running a new process from the .netstandard2.0 source generator as a work around to problem 2 above, it becomes clear that if someone were to switch out the path for c2cs they would then be running their own swapped out program. This sounds risky as it opens up the door to abuse. Running C2CS once to generate the bindings may be an extra step that could be automated away with source generators but that seems rather foolish as the benefits of source generators for C2CS in my honest opinion don't seem to be worth that risk. To be clear, source generators do solve specific problems to which the benefits out weight the risks, but in the context of C2CS I don't believe this to be the case.

Reduce filesize

Try to get C2CS down in file size by using nAOT. If that's not reasonable after that, check if we can build libclang ourselves with the minimum amount of code necessary.

Function pointers: implicit cast

Function proto mapping:

/** Action callback for systems and triggers */
typedef void (*ecs_iter_action_t)(
    ecs_iter_t *it);
[StructLayout(LayoutKind.Explicit, Size = 8, Pack = 8)]
public struct ecs_iter_action_t
{
    [FieldOffset(0)] // size = 8, padding = 0
    public delegate* unmanaged<ecs_iter_t*, void> Pointer;
}

Right now, it is used like this:

var callback = default(ecs_iter_action_t);
callback.Pointer = &CallbackMethod;
...
[UnmanagedCallersOnly]
public static void CallbackMethod(ecs_iter_t* iterator)
{
    ...
}

It would be nice to be used like this:

var callback = default(ecs_iter_action_t);
callback = &CallbackMethod;
...
[UnmanagedCallersOnly]
public static void CallbackMethod(ecs_iter_t* iterator)
{
    ...
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.