C++ 调用 C++ 库函数但执行了错误的函数

Posted

技术标签:

【中文标题】C++ 调用 C++ 库函数但执行了错误的函数【英文标题】:C++ calling a C++ library function but the wrong function is executed 【发布时间】:2022-01-22 04:42:02 【问题描述】:

我正在从我的应用程序中调用库类的函数,并且正在执行同一类中的不同函数。这是一个非常大的程序,带有 android c# 调用 c++ 函数。那部分工作正常。 c++ 代码正在调用动态 (*.so) 库,但正在执行错误的函数。 在 VS2019 中工作,适用于库代码和应用程​​序代码的 Clang 5.0 工具集。库源代码来自第三方,但由我移植和编译。相同的代码在 Windows 中运行良好。 以下是相关代码的浓缩。

包括库和应用代码共享的文件内容:

假设指向的项目(即 FileHeader 等)在包含文件中声明。

namespace nsLibraryNameSpc

    class Iitems
    
    public:
        virtual ~Iitems(void) ;
        virtual  int GetAnItem( int instance, unsigned itemID, char *fieldName ) = 0;
    ;  
    class Itable
    
    public:
        virtual ~Itable(void) ;
        virtual int GetBaseInfo( BaseInfo *pBI ) = 0;
        virtual int GetTablePtr( MainTableContents *pMTC ) = 0;
    ;  
    class Imethods
    
    public:
        virtual ~Imethods(void) ;
        virtual int StartMethod( unsigned methodNumber ) = 0;
        virtual int CancelMethod( unsigned methodNumber ) = 0;
    ;  
    class Imyclass : public Iitems, public Itable, Imethods
    
        public:
        Imyclass( void ) ;
        virtual ~Imyclass(void) ;
        
        virtual void GetFileHeader( FileHeader** pFh ) = 0;
        virtual  int AddFileHeader( const char* fileNm, const FileHeader * pFh ) = 0;
        virtual void *testCall02( void *p1, void *p2 ) = 0;
        virtual void *testCall01( void ) = 0;
    ;

库源代码:

假设类 Iitems、Itable 和 Imethods 中的方法存在于库中。

假设 LOG(char *fmt,...) 会将字符串记录到文件中。

MyClass.cpp

class MyClass : public Imyclass

private:
    FileHeader *pHeader;
    unsigned activeMethod;
    
public:
        MyClass( int instance )
        
            pHeader = 0;
            activeMethod = 0;
               
        virtual ~MyClass() 
        
            if (pHeader != 0)
                delete pHeader;
               
        virtual void GetFileHeader( FileHeader** pFh )
        
            LOG("Inside GetFileHeader\n");
            if (pFh != 0)
                *pFh = pHeader;
            return;
               
        virtual  int AddFileHeader( const char* fileNm, const FileHeader * pFh )
               
            LOG("Inside AddFileHeader\n");
            //if the file exists then load the file header else return -1;
            LOG("AddFileHeader calling testCall01\n");
            testCall01();
            LOG("Exiting AddFileHeader\n");
            return 0;       
               
        virtual void *testCall01( void ) 
        
            LOG("Inside testCall01\n");
            return 0;
               
        virtual void *testCall02( void *p1, void *p2 )
        
            LOG("Inside testCall02\n");
            return 0;
               
        int GetBaseInfo( BaseInfo *pBI )
        
            // some work
            return 0;
        
        int GetTablePtr( MainTableContents *pMTC )
        
            // some work
            return 0;
                       
        int StartMethod( unsigned methodNumber )
        
            // some work
            return 0;
        
        int CancelMethod( unsigned methodNumber )
        
            // some work
            return 0;
               
        int GetAnItem( int instance, unsigned itemID, char *fieldName )
        
            // some work
            return 0;
        


static int instCntr = 0;

Imyclass *makeMyClass(void)

    Imyclass * pRet = (Imyclass *) new MyClass(++instClass);
    return pRet;

Android 应用的 C++ 代码:

int testLibraryCalls(void)

    Imyclass *pclass = makeMyClass();
    
    LOG("In testLibraryCalls\n");
    pclass->testCall02( (void*)0, (void*)0 );
    LOG("testCall02 finished\n");
    pclass->testCall01();
    LOG("testCall01 finished\n");
    pclass->GetFileHeader(0);
    LOG("GetFileHeader finished\n");
    AddFileHeader( (void*)0, (void*)0 );
    LOG("AddFileHeader finished\n");

日志文件显示:

In testLibraryCalls
Inside AddFileHeader
AddFileHeader calling testCall01
Inside testCall01
Exiting AddFileHeader
testCall02 finished
Inside testCall02
testCall01 finished
GetFileHeader finished
Inside GetFileHeader
AddFileHeader finished

所以调用testCall02就是在执行AddFileHeader,

AddFileHeader 正在正确调用 testCall01(在与库代码相同的类中)。

从应用调用 testCall01 会在库中执行 testCall02。

从应用程序调用 GetFileHeader 要么不执行,要么调用没有 LOG 语句的东西。

调用 AddFileHeader 会执行库中的 GetFileHeader。

我已验证这台电脑上只有一个 .so 文件,其 nm 输出中列出了违规功能。

对我来说,这看起来像是库加载器故障,但我不敢相信。关于哪里出了问题或如何确定根本原因的任何想法都会非常有用。

================================================ ================

更新:我将这个***动态库变成了静态库 (*.a) 并将其链接到主应用程序。不用找了。呼叫仍然转到相同的错误功能。所以运行时链接不是问题的一部分。

【问题讨论】:

避免强制转换:(Imyclass *) new MyClass(++instClass);(Imyclass *) 这里不需要(并且可以在编码错误的情况下隐藏问题)。 Jarod42 - 错字,抱歉。已编辑。 使用override 仍然是可取的。 您的LOG 函数可能会以某种奇怪的方式重新排序输出。请阅读minimal reproducible example,然后阅读edit您的问题。 【参考方案1】:

通过使用语法(来自***):

void ( Imyclass::* pHeader)(FileHeader* pFileHeader) = &Imyclass::GetFileHeader;
void* pGetFileHeader = (void*&)pHeader;
LOG("==== %p from virtual GetFileHeader", pGetFileHeader);

int ( Imyclass::* pFunc)(const char *, const FileHeader ) = &Imyclass::AddFileHeader;
void* pAddHeader = (void*&)pFunc;
LOG("==== %p from virtual AddFileHeader", pAddHeader);
// these returned an offset into the class's virtual function table

并在它返回之前将其插入库的 makeMyClass 并插入一个类似的对 makeMyClass 从库返回到应用程序后,我发现虚拟 应用程序中的函数表关闭了 4 个字节(一个指针)。

事实证明,我在 Iitems 类示例中遗漏了一个虚函数。 我忽略了它,因为它位于应用程序中未定义的#ifdef 中。

它是在库中定义的。

class Iitems

public:
    virtual ~Iitems(void) ;
    virtual  int GetAnItem( int instance, unsigned itemID, char *fieldName ) = 0;
#ifdef _DEBUG
    virtual void DisplayString(char* str);
#endif
;  

在应用中定义 _DEBUG 解决了这个问题。

【讨论】:

以上是关于C++ 调用 C++ 库函数但执行了错误的函数的主要内容,如果未能解决你的问题,请参考以下文章

C++:动态共享库中的虚函数产生段错误

C++派生类调用函数

使用扩展参数调用 Delphi 函数时出现 C++ 错误

c++ 错误:没有匹配的函数用于从函数内调用“getline”,但在 main 中有效

Python绑定C++虚成员函数不能调用

带有指针参数的 C++ 函数