首页 > 解决方案 > 我应该如何让用户定义库中的函数?

问题描述

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

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

在您将定义的用户代码app_main(){...}中,只需链接到库,您就可以对其进行编译并执行任何必要的操作,而无需用户对其进行交互并使其运行。

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

我不认为这是一种“好”的做事方式,但我也不知道还有什么其他的方式去做。


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

我的 Lib 提供了 android 入口点,并且现在要求将用户 main() 设置为 extern。 我的库

// 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;}

我的应用程序.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)

一起编译代码时,它会按预期构建和运行。但是当编译为单独的库时,将 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"...

为了使它不那么密集(我觉得可能是这样),我使用了一些外部模块来查找目录,定义其他内容,如果您认为有必要,我将很快编辑并链接到某个地方。

希望这能让我更容易理解我的方法以及我想要完成的工作,即从我定义的 android_main 或我肯定会错过的类似方法中调用“用户定义的 Main”。


第二次编辑:以防万一我在 Linux (gcc.g++ 9.3.0) 上尝试了相同的方法,它可以按我的预期工作,但它可能不是一个好方法。

我的库

// 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 true;} 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)

标签: c++android-ndkshared-librariesstatic-librariesextern

解决方案


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

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

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

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

  • -ffunctions-sections/fdata-sections= 从字面上看,我对他们一无所知。
  • -Wall= 打开所有可选警告,我可能在调试版本中需要它
  • -fvisibility=hidden= 使事物不可用,除非被标记为可导出的事物标记(在我的情况下是 MyLib 定义),它会影响链接,但我并不真正理解它。
  • -Wl= 用于将选项传递给以逗号分隔的链接器
  • --gc-section= 与 -Wl 一起传递的链接器选项,如果符号未定义,则可以清除符号,这可能是一个原因,这在 MyLib myapp_main 上未定义但在 MyApp 上定义。
  • fPIC= 设置与位置无关的代码,适用于共享库,我相信它会为函数创建一个查找表(全局偏移表,使目标操作系统的动态加载器更容易)。
  • -s=我没有找到任何关于它的东西,它可能与-static有关吗?如果是这样,这可能是一个问题,因为我正在构建共享库。

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

感谢您提供有关制作最小可重现示例的指南,它对我找到需要关注的事情有很大帮助!


推荐阅读