├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── demo.cpp ├── hackdemo.cpp └── include ├── funcptr.hpp └── hackptr.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | compile_commands.json 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | 3 | project(funcptr) 4 | 5 | add_library(funcptr INTERFACE) 6 | target_include_directories(funcptr INTERFACE include) 7 | 8 | add_executable(demo demo.cpp) 9 | target_link_libraries(demo funcptr) 10 | set_property(TARGET demo PROPERTY CXX_STANDARD 17) 11 | 12 | add_executable(hackdemo hackdemo.cpp) 13 | target_link_libraries(hackdemo funcptr) 14 | set_property(TARGET hackdemo PROPERTY CXX_STANDARD 17) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FuncPtr 2 | 3 | A bridge for convert c++ function object to c function pointer (with user pointer) 4 | 5 | Have you ever lost your head in passing a function to the C interface? Are you still fighting against `void *` ? With funcptr, you no longer have to worry about passing C++ functions in the C interface 6 | 7 | ## Usage 8 | 9 | ```c++ 10 | void some_c_interface(int arg, void(*callback)(int input, void *user), void *user); 11 | 12 | using namespace func_ptr; 13 | int captured; 14 | funcptr lambda{[&](int input /* Normal parameter */, user_pointer /* Use this for user pointer placeholder */) { process(input, captured); }}; 15 | some_c_interface(arg /* Normal parameter */, lambda, lambda.holder() /* User pointer */); 16 | ``` 17 | 18 | It support 19 | 20 | 1. Pass normal function to c interface (Not recommanded, please pass it directly) 21 | 2. Pass std::function to c interface 22 | 3. Pass lambda and std::bind 23 | 24 | ## Lifetime 25 | 26 | All funcptr 27 | 28 | ## TODO 29 | 30 | 1. Add a asm magic for pass c++ function to c interface that no user pointer. 31 | 32 | ## LICENSE 33 | 34 | Unlicensed see [LICENSE](LICENSE); 35 | -------------------------------------------------------------------------------- /demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "include/funcptr.hpp" 5 | 6 | using namespace func_ptr; 7 | 8 | void fake_clib_func(void (*callback)(int input, void *user), void *user) { 9 | std::cout << "before" << std::endl; 10 | callback(42, user); 11 | std::cout << "after" << std::endl; 12 | } 13 | void another_clib_func(void (*callback)(void *user, int input), void *user) { 14 | std::cout << "[another] before" << std::endl; 15 | callback(user, 233); 16 | std::cout << "[another] after" << std::endl; 17 | } 18 | 19 | void test(int input, user_pointer) { 20 | std::cout << "got " << input << std::endl; 21 | } 22 | 23 | int main() { 24 | funcptr fn{test}; 25 | fake_clib_func(fn, fn.holder()); 26 | 27 | funcptr fnobj{std::function{test}}; 28 | fake_clib_func(fnobj, fnobj.holder()); 29 | 30 | int binding; 31 | 32 | funcptr lambda{[&](int input, user_pointer) { binding = input; }}; 33 | fake_clib_func(lambda, lambda.holder()); 34 | 35 | std::cout << "[main] got " << binding << std::endl; 36 | 37 | funcptr another_lambda{[&](user_pointer, int input) { binding = input; }}; 38 | another_clib_func(another_lambda, another_lambda.holder()); 39 | 40 | std::cout << "[main] got " << binding << std::endl; 41 | } -------------------------------------------------------------------------------- /hackdemo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "include/hackptr.hpp" 5 | 6 | using namespace func_ptr; 7 | 8 | void fake_clib_func(void (*callback)(int input)) { 9 | std::cout << "before" << std::endl; 10 | callback(42); 11 | std::cout << "after" << std::endl; 12 | } 13 | 14 | int main() { 15 | hackfunc fn{ 16 | [](int input) { std::cout << "directly: " << input << std::endl; }}; 17 | 18 | fake_clib_func(fn); 19 | 20 | int binding; 21 | 22 | hackfunc fn2{ 23 | [&](int input) { binding = input; }}; 24 | 25 | fake_clib_func(fn2); 26 | std::cout << "[main] got " << binding << std::endl; 27 | } -------------------------------------------------------------------------------- /include/funcptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace func_ptr { 9 | 10 | struct user_pointer { 11 | void *value; 12 | 13 | user_pointer(void *value) : value(value) {} 14 | 15 | template inline target *cast() { 16 | target *ret; 17 | memcpy(&ret, &value, sizeof value); 18 | return ret; 19 | } 20 | }; 21 | 22 | namespace details { 23 | 24 | template struct always_false : std::false_type {}; 25 | 26 | template struct user_pointer_transform { using type = src; }; 27 | template <> struct user_pointer_transform { 28 | using type = void *; 29 | }; 30 | 31 | template 32 | using user_pointer_transform_t = typename user_pointer_transform::type; 33 | 34 | template 35 | using funcproto_transform_t = ret (*)(user_pointer_transform_t...); 36 | 37 | template struct has_user_pointer; 38 | 39 | template <> struct has_user_pointer<> : std::false_type {}; 40 | template <> struct has_user_pointer : std::true_type {}; 41 | template 42 | struct has_user_pointer : std::true_type {}; 43 | template 44 | struct has_user_pointer : has_user_pointer {}; 45 | 46 | template 47 | inline constexpr bool has_user_pointer_v = has_user_pointer::value; 48 | 49 | template struct user_pointer_trait; 50 | 51 | template struct user_pointer_trait { 52 | inline static user_pointer get_user_pointer(user_pointer ptr, rest...) { 53 | return ptr; 54 | } 55 | }; 56 | template 57 | struct user_pointer_trait { 58 | inline static user_pointer get_user_pointer(first, rest... args) { 59 | return user_pointer_trait::get_user_pointer(args...); 60 | } 61 | }; 62 | template <> struct user_pointer_trait { 63 | inline static user_pointer get_user_pointer(user_pointer ptr) { return ptr; } 64 | }; 65 | template struct user_pointer_trait { 66 | static_assert(always_false::value, 67 | "user_pointer not appear in the argument list"); 68 | }; 69 | } // namespace details 70 | 71 | template struct funcptr; 72 | 73 | template struct funcptr { 74 | static_assert(details::has_user_pointer_v, 75 | "Require at least one user_pointer argument"); 76 | using proto = details::funcproto_transform_t; 77 | using input = ret (*)(params...); 78 | inline constexpr explicit funcptr(input src) : src(src) {} 79 | inline constexpr operator proto() const { 80 | return +[](details::user_pointer_transform_t... ps) -> ret { 81 | user_pointer ptr = 82 | details::user_pointer_trait::get_user_pointer(ps...); 83 | return (ptr.cast())(std::forward(ps)...); 84 | }; 85 | } 86 | inline constexpr void *holder() const { return (void *)src; } 87 | 88 | private: 89 | input src; 90 | }; 91 | 92 | template 93 | struct funcptr> { 94 | static_assert(details::has_user_pointer_v, 95 | "Require at least one user_pointer argument"); 96 | using proto = details::funcproto_transform_t; 97 | using input = std::function; 98 | inline explicit funcptr(input &&src) : storage(src) {} 99 | inline operator proto() { 100 | return [](details::user_pointer_transform_t... ps) -> ret { 101 | user_pointer ptr = 102 | details::user_pointer_trait::get_user_pointer(ps...); 103 | return (*ptr.cast())(std::forward(ps)...); 104 | }; 105 | } 106 | inline void *holder() { return &storage; } 107 | 108 | private: 109 | input storage; 110 | }; 111 | 112 | template 113 | struct funcptr()))>> 115 | : funcptr()))> { 116 | funcptr(lambda lam) 117 | : funcptr()))>(lam) {} 118 | }; 119 | 120 | template 121 | funcptr(ret (*)(params...)) -> funcptr; 122 | 123 | template 124 | funcptr(std::function) 125 | -> funcptr>; 126 | 127 | template funcptr(lambda lam) -> funcptr; 128 | 129 | } // namespace func_ptr -------------------------------------------------------------------------------- /include/hackptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // clang-format off 7 | #if defined(__x86_64__) || defined(_M_AMD64) 8 | # define FUNCPTR_ARCH_X86_64 9 | #elif defined(__i386__) || defined(_M_IX86) 10 | # define FUNCPTR_ARCH_X86 11 | #else 12 | # error Not support target arch 13 | #endif 14 | 15 | #if defined(__linux__) 16 | # define FUNCPTR_OS_LINUX 17 | #elif defined(_WIN32) 18 | # define FUNCPTR_OS_WINDOWS 19 | #else 20 | # error Not support target os 21 | #endif 22 | 23 | #if defined(__GNUC__) 24 | # define FUNCPTR_COMPILER_GCC 25 | #elif defined(_MSC_VER) 26 | # define FUNCPTR_COMPILER_MSVC 27 | #else 28 | # error Not support compiler 29 | #endif 30 | 31 | #if defined(FUNCPTR_COMPILER_GCC) 32 | # define FUNCPTR_PACK_BEFORE 33 | # define FUNCPTR_PACK_AFTER __attribute__((packed)) 34 | #elif defined(FUNCPTR_COMPILER_MSVC) 35 | # define FUNCPTR_PACK_BEFORE __pragma(pack(push, 1)) 36 | # define FUNCPTR_PACK_AFTER ;__pragma(pack(pop)) 37 | #endif 38 | 39 | #if defined(FUNCPTR_OS_LINUX) 40 | # include 41 | #elif defined(FUNCPTR_OS_WINDOWS) 42 | # include 43 | #endif 44 | // clang-format on 45 | 46 | namespace func_ptr { 47 | 48 | namespace details { 49 | 50 | template struct hackfunc_impl; 51 | 52 | #if defined(FUNCPTR_ARCH_X86_64) 53 | 54 | FUNCPTR_PACK_BEFORE 55 | template struct alignas(8) hackfunc_impl { 56 | short mov_r10 = 0xba49; 57 | std::function *func; 58 | short mov_rax = 0xb848; 59 | void *ptr = (void *)wrapper; 60 | short jmp = 0xe0ff; 61 | 62 | hackfunc_impl(std::function &fn) : func(&fn) {} 63 | 64 | static auto wrapper(params... ps) { 65 | std::function *func; 66 | asm("movq %%r10, %0" : "=r"(func)); 67 | return (*func)(ps...); 68 | } 69 | } FUNCPTR_PACK_AFTER; 70 | 71 | #else 72 | #error TODO 73 | #endif 74 | 75 | void *alloc_executable_memory(); 76 | 77 | #if defined(FUNCPTR_OS_LINUX) 78 | 79 | void *alloc_executable_memory() { 80 | return mmap(0, sizeof(hackfunc_impl), 81 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, 82 | -1, 0); 83 | } 84 | 85 | #elif defined(FUNCPTR_OS_WINDOWS) 86 | 87 | void *alloc_executable_memory() { 88 | return VirtualAlloc(nullptr, sizeof(hackfunc_impl), MEM_COMMIT, 89 | PAGE_EXECUTE_READWRITE); 90 | } 91 | 92 | #else 93 | #error TODO 94 | #endif 95 | 96 | } // namespace details 97 | 98 | template struct hackfunc; 99 | 100 | template 101 | struct hackfunc> { 102 | using proto = ret (*)(params...); 103 | using input = std::function; 104 | 105 | hackfunc(input fn) : storage(fn) {} 106 | 107 | operator proto() { 108 | auto temp = new (details::alloc_executable_memory()) 109 | details::hackfunc_impl(storage); 110 | proto swap; 111 | memcpy(&swap, &temp, sizeof swap); 112 | return swap; 113 | } 114 | 115 | private: 116 | input storage; 117 | }; 118 | 119 | template 120 | struct hackfunc()))>> 122 | : hackfunc()))> { 123 | hackfunc(lambda lam) 124 | : hackfunc()))>(lam) {} 125 | }; 126 | 127 | template 128 | hackfunc(std::function) 129 | -> hackfunc>; 130 | 131 | template hackfunc(lambda lam) -> hackfunc; 132 | 133 | } // namespace func_ptr --------------------------------------------------------------------------------