我应该如何着手让用户定义库中的函数?

How should I go about making a function from a library be defined by a user?

为了说明一些上下文,我尝试创建一个库,它定义了一个入口点,然后会调用用户定义的 main 来执行实际的用户应用程序。

extern int app_main();
mylib_main(struct android_app* app)
{
...
    // call app_main()
...
}

在用户代码中,您可以定义 app_main(){...},只需 link 进入库,您就可以对其进行编译,并在无需用户交互的情况下执行任何必要的操作并获取它 运行宁.

现在的问题是,如果所有代码都编译为一个库,它可以 运行,因为它确实在源代码中找到了 extern 函数。 但是,如果我将“my_library”编译成一个实际的共享库,并将用户应用程序编译成另一个 link 反对前者的共享库或可执行文件,它就不会了。

我不认为这是处理事情的“好”方法,但我也不知道还有什么其他方法可以处理。


编辑:最小可重现示例(我不知道这一点,感谢您指出这是一个很好的做法):

My Lib 提供了 android 入口点并要求用户 main() 立即设置为外部。 MyLib.h

// Export Markers
#ifndef MyLib
#   ifdef _WIN32
#       if defined(FL_BUILD_SHARED) /* build dll */
#           define MyLib __declspec(dllexport)
#       elif !defined(FL_BUILD_STATIC) /* use dll */
#           define MyLib __declspec(dllimport)
#       else /* static library */
#           define MyLib
#       endif
#   else
#       define MyLib
#   endif
#endif

MyLib bool MyLibTest();
MyLib void PrintLog (int lt, const char* log);

MyLib.cpp

#include "mylib.h"

#include <android_native_app_glue.h>
#include <android/log.h>

void handle_android_cmd(struct android_app* app, int32_t cmd)
{}
int32_t handle_android_input(struct android_app* app, AInputEvent* ev)
{return 0;}

extern int main();

void PrintLog(int lt, const char* log)
{
    __android_log_print(lt, "MyAppTest", log);
}

void android_main(struct android_app* app)
{
    PrintLog(4, "Android Flylib Start"); //4 is Info Log

    app->onAppCmd = handle_android_cmd;
    app->onInputEvent = handle_android_input;

    main(); // Call the user main
}

bool MyLibTest() {return true;}

MyApp.cpp

#include <mylib.h>

int main()
{
    bool test = false;
    test = MyLibTest();
    if(test == false)
        return 1;
    else
        return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.1) #require a minimum cmake version to build

project(MyApp LANGUAGES C CXX)

set(CMake_Modules ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
include(${CMake_Modules}/Helpers.cmake)
include(${CMake_Modules}/FindAndroidSDKVars.cmake)
include(${CMake_Modules}/SetupAndroidEnv.cmake) # Sets up compiler, flags, and directory variables for android NDK

add_library(MyApp SHARED ${CMAKE_CURRENT_SOURCE_DIR}/MyApp/MyApp.cpp)
file(GLOB MyLib_SRC ${CMAKE_CURRENT_SOURCE_DIR}/MyLib/*.cpp)

set(APP_GLUE_SRC "${NDK}/sources/android/native_app_glue/android_native_app_glue.c")
set(NDKINCLUDE ${NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include)

if(SEPARATE_BUILD)
    add_library(MyLib SHARED "${MyLib_SRC}" ${APP_GLUE_SRC})
    target_include_directories(MyLib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/MyLib)

    target_include_directories(MyLib PRIVATE 
        ${NDKINCLUDE} 
        ${NDKINCLUDE}/android
        ${NDKINCLUDE}/${ANDROID_PLATFORM}
        ${NDK}/sources/android/native_app_glue
        ${NDK}/sources/android/cpufeatures
    )
    target_link_libraries(MyLib PRIVATE
        ${LIBLINK}/libm.so
        ${LIBLINK}/libandroid.so 
        ${LIBLINK}/liblog.so
        ${LLVM_LIBC++}
    )
    target_link_libraries(MyApp PUBLIC MyLib)
else()
    target_sources(MyApp PUBLIC "${MyLib_SRC}" ${APP_GLUE_SRC})
    target_include_directories(MyApp PRIVATE 
        ${NDKINCLUDE} 
        ${NDKINCLUDE}/android
        ${NDKINCLUDE}/${ANDROID_PLATFORM}
        ${NDK}/sources/android/native_app_glue
        ${NDK}/sources/android/cpufeatures
    )
    target_link_libraries(MyApp PRIVATE
        ${LIBLINK}/libm.so
        ${LIBLINK}/libandroid.so 
        ${LIBLINK}/liblog.so
        ${LLVM_LIBC++}
    )
endif()
message(STATUS ${APP_GLUE_SRC})

target_include_directories(MyApp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/MyLib)

一起编译代码时,它会按预期构建和 运行s。 但是当编译为单独的库时,link将 MyApp 转换为 MyLib 然后无法找到函数 MyLibTest(我相信这会发生在任何从 main 调用的函数上)。

java.lang.UnsatisfiedLinkError: Unable to load native library "/data/app/org.MyAppOrg.MyApp-vqyEUo8t0onsU89Q6ScbNw==/lib/arm64/libMyApp.so": dlopen failed: cannot locate symbol "_Z9MyLibTestv" referenced by "/data/app/org.MyAppOrg.MyApp-vqyEUo8t0onsU89Q6ScbNw==/lib/arm64/libMyApp.so"...

为了降低密度(我觉得可能是这样),我使用了一些外部模块来查找目录、定义其他内容,如果您认为有必要,我将很快编辑并 link 到某处。

希望这能让我更容易理解我的方法和我想要完成的事情,即从我定义的 android_main 或我肯定缺少的类似方法中调用“用户定义的 Main” .


第二次编辑:以防万一我对Linux(gcc.g++ 9.3.0)尝试了相同的方法并且它像我一样工作有意为之,但这可能不是一个好方法。

MyLib.h

// Export Markers
#ifndef MyLib
#   ifdef _WIN32
#       if defined(FL_BUILD_SHARED) /* build dll */
#           define MyLib __declspec(dllexport)
#       elif !defined(FL_BUILD_STATIC) /* use dll */
#           define MyLib __declspec(dllimport)
#       else /* static library */
#           define MyLib
#       endif
#   else
#       define MyLib
#   endif
#endif

MyLib bool MyLibTest();

MyLib.cpp

#include "mylib.h"

#include <iostream>

extern int myapp_main();

int main()
{
    std::cout << "MyLib Start";

    myapp_main(); // Call the user main
}

bool MyLibTest() {return 真;} MyApp.cpp

#include <mylib.h>
#include <iostream>

int myapp_main()
{
    std::cout << "MyApp Test";
    bool test = false;
    test = MyLibTest();
    if(test == false)
        return 1;
    else
        return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.1) #require a minimum cmake version to build

project(MyApp LANGUAGES C CXX)

set(CMake_Modules ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
include(${CMake_Modules}/Helpers.cmake)

add_executable(MyApp ${CMAKE_CURRENT_SOURCE_DIR}/MyApp/main.cpp)
file(GLOB MyLib_SRC ${CMAKE_CURRENT_SOURCE_DIR}/MyLib/*.cpp)

if(SEPARATE_BUILD)
    add_library(MyLib SHARED "${MyLib_SRC}")
    target_include_directories(MyLib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/MyLib)

    target_link_libraries(MyApp PUBLIC MyLib)
else()
    target_sources(MyApp PUBLIC "${MyLib_SRC}")
endif()
message(STATUS ${APP_GLUE_SRC})

target_include_directories(MyApp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/MyLib)

出于学习目的回答自己的问题: 对于 android 的构建细节,我在另一个 makefile 中设置了环境。因为我一直是通过从各种来源学习来制作它的,所以其中一些是复制粘贴的,并没有考虑太多,就好像没有解释它们是应该起作用的东西一样。

我尝试的是隔离所有我不知道它们为什么存在的选项,在我的例子中是设置特定的标志,这些是特定的:

-ffunction-sections -fdata-sections -Wall -fvisibility=hidden -fPIC -Wl --gc-sections -s

在没有指定它们之后,构建按预期工作,但我不知道这些标志中的哪些做了什么以及为什么会导致不工作,所以我得到了一些关于这些 flags/options 的信息,考虑帐户 htis 是我的理解,可能不正确。

  • -ffunctions-sections/fdata-sections = 从字面上看,我对他们一无所知。
  • -Wall = 关闭所有可选警告,我可能需要在调试版本中使用它
  • -fvisibility=hidden = 使事物不可用,除非用将它们标记为 exportable 的事物标记(在我的情况下是 MyLib 定义),它会影响链接,但我不太明白它。
  • -Wl = 用于将选项传递给以逗号分隔的链接器
  • --gc-section = 使用 -Wl 传递的链接器选项,如果符号未定义,则可以清除符号,这在 MyLib myapp_main 上未定义但在 MyApp 上定义,可能是一个原因。
  • fPIC = 设置与位置无关的代码,suitable 用于共享库,我认为它创建的功能类似于查找 table(全局偏移 Table ,使目标 OS' 动态加载器更容易)。
  • -s = 没查到,会不会跟-static有关?如果是这样,那可能是一个问题,因为我正在构建共享库。

最后 -fvisibility=hidden 是对预期行为产生负面影响的选项。如果有人可以扩展影响这种方式的实际发生的事情以及我屠杀的任何 flag/option,我将不胜感激。

感谢您提供制作 Minimal Reproducible Example 的指南,它帮助我找到了需要关注的事情!