Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ffi #22

Closed
DrYaling opened this issue Dec 7, 2022 · 2 comments
Closed

ffi #22

DrYaling opened this issue Dec 7, 2022 · 2 comments

Comments

@DrYaling
Copy link

DrYaling commented Dec 7, 2022

Hi MaikKlein.
I've tried to use cxx to build FFI wrappers, but I found this way is almost on the opposite way to bind ue apis to rust.
Then I tried another way, using clang ast-dump tool to find "all" the "Runtime/" headers, then export all(maybe) these safe apis and generate FFI bind wrappers from cpp and rust. But it's still very experimental.
I'm unreal newer, but do not like to build or develop a game using c++.Hope this will give you some help.
Blow are the examples:
rust side:

use std::{ffi::c_void, os::raw::c_char};
use ffis::*;
pub type UObjectOpaque = c_void;//cpp type UObject
pub struct UObject{
    inner: *mut UObjectOpaque
}
impl UObject{
    #[inline]
    pub fn AbortInsideMemberFunction(&self){
        unsafe{ UObject_AbortInsideMemberFunctionInvokerHandler.as_ref().unwrap()(self.inner) }
    }
    #[inline]
    pub fn AreNativePropertiesIdenticalTo(&self, Other: *mut UObjectOpaque) -> bool{
        unsafe{ UObject_AreNativePropertiesIdenticalToInvokerHandler.as_ref().unwrap()(self.inner, Other) }
    }
}
mod ffis{
    use super::*;
    type UObject_AbortInsideMemberFunctionInvoker = unsafe extern "C" fn(*mut c_void);
    pub(super) static mut UObject_AbortInsideMemberFunctionInvokerHandler: Option<UObject_AbortInsideMemberFunctionInvoker> = None;
    #[no_mangle]
    extern "C" fn set_UObject_AbortInsideMemberFunction_handler(handler: UObject_AbortInsideMemberFunctionInvoker){
        unsafe{ UObject_AbortInsideMemberFunctionInvokerHandler = Some(handler) };
    }

    type UObject_AreNativePropertiesIdenticalToInvoker = unsafe extern "C" fn(*mut c_void, *mut UObjectOpaque) -> bool;
    pub(super) static mut UObject_AreNativePropertiesIdenticalToInvokerHandler: Option<UObject_AreNativePropertiesIdenticalToInvoker> = None;
    #[no_mangle]
    extern "C" fn set_UObject_AreNativePropertiesIdenticalTo_handler(handler: UObject_AreNativePropertiesIdenticalToInvoker){
        unsafe{ UObject_AreNativePropertiesIdenticalToInvokerHandler = Some(handler) };
    }
}

cpp side Rigister:

//#include "CoreMinimal.h"
//#include "Binder.h"
using uapi_UObject_AbortInsideMemberFunctionFn = void(*)(void* target);
using uapi_UObject_AreNativePropertiesIdenticalToFn = bool(*)(void* target,UObject* Other);
class Plugin{
    public:
    void* GetDllExport(FString apiName);
}
//auto generated register entry
void register_all(Plugin* plugin){    
    auto const apiuapi_UObject_AbortInsideMemberFunction = (uapi_UObject_AbortInsideMemberFunctionFn)plugin->GetDllExport(TEXT("set_UObject_AbortInsideMemberFunction_handler\0"));
    if(apiuapi_UObject_AbortInsideMemberFunction){
        apiuapi_UObject_AbortInsideMemberFunction(&uapi_UObject_AbortInsideMemberFunction);
    }

    auto const apiuapi_UObject_AreNativePropertiesIdenticalTo = (uapi_UObject_AreNativePropertiesIdenticalToFn)plugin->GetDllExport(TEXT("set_UObject_AreNativePropertiesIdenticalTo_handler\0"));
    if(apiuapi_UObject_AreNativePropertiesIdenticalTo){
        apiuapi_UObject_AreNativePropertiesIdenticalTo(&uapi_UObject_AreNativePropertiesIdenticalTo);
    }
}

cpp side Binder:

//#include "CoreUObject/Public/UObject/Object.h"
extern "C"{
void uapi_UObject_AbortInsideMemberFunction(void* target){
        ((UObject*)target)->AbortInsideMemberFunction();
    }
bool uapi_UObject_AreNativePropertiesIdenticalTo(void* target, UObject* Other){
        return  ((UObject*)target)->AreNativePropertiesIdenticalTo(Other);
    }
}

The project url is https://github.com/DrYaling/UnrealObject2RustBuilder.git

@MaikKlein
Copy link
Owner

MaikKlein commented Dec 11, 2022

Thanks for looking into this!

This is also the approach https://github.com/EmbarkStudios/physx-rs uses. Out of curiosity, why clang-ast over using libclang directly? https://github.com/KyleMayes/clang-rs

I'll have a closer look at it during Christmas, but this is promising ❤️

@DrYaling
Copy link
Author

Thanks for looking into this!

This is also the approach https://github.com/EmbarkStudios/physx-rs uses. Out of curiosity, why clang-ast over using libclang directly? https://github.com/KyleMayes/clang-rs

I'll have a closer look at it during Christmas, but this is promising ❤️

I tried to use bindgen/cbindgen or cxx to build FFI APIs for rust, but found it's hard to(or impossible) build unreal source code for all these 3rd-plugin based on LLVM(or clang).So I have to find another way, try not to build unreal source code, but only parse the Header AST and extract the APIs and Objects we needed.
But the unreal-engine headers with so many MACROs and Attributes increase the difficulty of parsing with clang ast-dump, then I remove most of the unreal MACROs and Attributes.
As for clang-ast, I seldom use compile tools like LLVM or clang, my first idea was to write the parse code by myself until I found it's unrealistic and not worth. Then I tried to find a easy way to extract the AST until I found this crate. In fact it's just a JSON serializer for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants