CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12
  1. #1
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    [RESOLVED] Shared Lib: Can const char * travel between client and shared libs?

    I'm making a shared lib application (currently being tested on Linux), and I just encountered a problem.

    The process goes like this.

    1) The client creates an std::string .
    2) Client sends the string as const char * using std::string::c_str() through an extern "C" function to the shared lib.
    3) The shared lib stores the const char * as an std::string.
    4) The client calls another extern "C" function which returns the shared lib's std::string back to the client (again using std::string::c_str() ).
    5) The client prints it using std::cout and I get some of the characters and some weird symbols.

    There must be something wrong with the "\n" character, it gets lost in the way.

    Is this a common problem in dlls? Do I have to dynamicly allocate memory for that string?

    I don't know how to give you any code, its too complicated. That string travels through a lot until it gets there, but I guarantee you that when it reaches the dll, the string is fine, the problem is when it returns back to client.


    Check this. The window title shows the string that is stored inside the shared lib (which was send by the client).
    The VampEngi�� in the console output is the printed string returned by the shared lib to the client.
    Name:  Screenshot_2019-08-29_17-31-53.jpg
Views: 285
Size:  11.4 KB

  2. #2
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: Shared Lib: Can const char * travel between client and shared libs?

    How is 3) done?

    I don't know how to give you any code, its too complicated.
    In cases like this, a good way forward is to produce minimal test case(s) that demonstrate the issue.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  3. #3
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Shared Lib: Can const char * travel between client and shared libs?

    Quote Originally Posted by 2kaud View Post
    How is 3) done?
    Using the initialization list of a constructor. I will try to reproduce a minimal test.

    Anything that starts with vampEngine... is an extern "C" function. In most of the files down below I will show only the important implementations which connect with the problem.

    The codes below are in order, in the same direction that the message travels.

    entryPoint.h
    Code:
    #ifdef VAMPENGINE_ENTRYPOINT
    #include <iostream>
    
    
    //This MUST be implemented by the client.
    namespace VampEngine
    {
        Application *createApp();
    }
    
    int main(int argc, char *argv[])
    {
        VampEngine::Application *app = VampEngine::createApp();
        app->core->MainLoop();
        delete app;
        
        return 0;
    }
    
    #endif

    Client Main. ( <VampEngine.h> includes the entryPoint)
    Code:
    #define VAMPENGINE_ENTRYPOINT
    #include <VampEngine.h>
    
    class Sandbox:public VampEngine::Application
    {
       
        //This is where the string "VampEngine" starts it's journey.
        public:
        Sandbox()
        : Application(400, 400, "VampEngine")
        {
            std::cout << core->window->getWidth() << std::endl;
            std::cout << core->window->getHeight() << std::endl;
            std::cout << core->window->getTitle() << std::endl;
        }
    
        ~Sandbox(){}
    };
    
    VampEngine::Application *VampEngine::createApp()
    {
        return new Sandbox();
    }

    Application.h This is the one that the client inherits in the code above. This gets compiled by the client.
    Code:
    #ifndef VAMPENGINE_APPLICATION_H
    #define VAMPENGINE_APPLICATION_H
    
    #include "client/core.h"
    
    namespace VampEngine
    {
        class Application
        {
            public:
            Core * const core;
    
            public:
            Application(unsigned int width, unsigned int height, std::string title)
            : core(new Core(width, height, title))
            {
            }
    
            virtual ~Application()
            {
                delete core;
            }
        };
    }
    
    #endif


    Core.hpp (Compiled by the client). This is a wrapper class. The implementation lives inside the shared lib.
    Code:
    #ifndef VAMPENGINE_CORE_HPP
    #define VAMPENGINE_CORE_HPP
    
    #include "client/core.h"
    #include "client/window.h"
    
    VampEngine::Core::Core(unsigned int width, unsigned int height, std::string title)
    {
        m_Data = vampEngineCoreConstructor(width, height, title.c_str());
        window = new Window();
        window->m_WindowImpl = m_Data->windowImpl;
    }
    #endif

    coreImpl.cpp (Compiled in the DLL).
    Includes the implementation of the extern "C" wrappers used in the previous wrapper class.
    Code:
    #include "coreImpl.h"
    #include "../client/core.h"
    #include "windowImpl.h"
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    
    VampEngine::CoreImpl::CoreImpl(unsigned int width, unsigned int height, std::string title)
        : m_Window(new VampEngine::WindowImpl(width, height, title))
    {
    
    }
    
    
    VampCoreData *vampEngineCoreConstructor(unsigned int width, unsigned int height, const char *title)
    {
    
        //Create a new CoreImpl object.
        VampEngine::CoreImpl *coreImpl = new VampEngine::CoreImpl(width, height, title);
    
        //Create Data.
        VampCoreData *data = new VampCoreData();
        data->coreImpl     = (void *)coreImpl;
        data->windowImpl   = (void *)coreImpl->m_Window;
    
        return data;
    }


    window.h (Compiled in the client) : Wrapper class of windowImpl.cpp.
    Code:
    #ifndef VAMPENGINE_WINDOW_H
    #define VAMPENGINE_WINDOW_H
    
    #include <iostream>
    
    #include "api.h"
    
    extern "C"
    {
        VAMPENGINE_EXPORT int vampWindowGetWidth(void *windowObj);
        VAMPENGINE_EXPORT int vampWindowGetHeight(void *windowObj);
        VAMPENGINE_EXPORT const char *vampWindowGetTitle(void *windowObj);
    }
    
    namespace VampEngine
    {
        class Window
        {
            friend class Core;
            
            public:
            Window();
            ~Window();
            
            inline unsigned int getWidth(){return vampWindowGetWidth(m_WindowImpl);}
            inline unsigned int getHeight(){return vampWindowGetHeight(m_WindowImpl);}
            inline std::string getTitle(){return std::string(vampWindowGetTitle(m_WindowImpl));}
    
            private:
            void *m_WindowImpl;
        };
    }
    
    #endif

    windowImpl.cpp (Compiled inside the DLL): This is the implementation of the class above.
    Code:
    #include "windowImpl.h"
    #include "../client/window.h"
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    
    VampEngine::WindowImpl::WindowImpl(unsigned int width, unsigned int height, std::string title)
        : m_Width(width), m_Height(height), m_Title(title)
    {
    }
    
    
    
    const char *vampWindowGetTitle(void *windowObj)
    {
        VampEngine::WindowImpl *self = (VampEngine::WindowImpl *)windowObj;
    
        return self->getTitle().c_str();
    }
    windowImpl.h
    Code:
    #ifndef VAMPENGINE_WINDOWIMPL_H
    #define VAMPENGINE_WINDOWIMPL_H
    
    #include <iostream>
    
    struct GLFWwindow;
    
    namespace VampEngine
    {
        class WindowImpl
        {
            friend class CoreImpl;
            
            public:
            WindowImpl(unsigned int width, unsigned int height, std::string title);
            ~WindowImpl();
            
            inline unsigned int getWidth() {return m_Width;}
            inline unsigned int getHeight() {return m_Height;}
            inline std::string getTitle() {return m_Title;}
    
            private:
            GLFWwindow *m_Window;
            unsigned int m_Width, m_Height;
            std::string m_Title;
        };
    }
    
    #endif
    By the way the windowImpl.cpp and coreImpl.cpp have their own header files. Don't get confused and think that core.hpp and window.h are the declerations of them. These header files are wrappers that get compiled in the client.
    Last edited by babaliaris; August 29th, 2019 at 10:46 AM.

  4. #4
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Shared Lib: Can const char * travel between client and shared libs?

    >> 4) The client calls another extern "C" function which returns the shared lib's std::string back to the client (again using std::string::c_str() ).

    c_str() returns the null terminated char array which is different than returning a std::string object.

  5. #5
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Shared Lib: Can const char * travel between client and shared libs?

    Quote Originally Posted by Arjay View Post
    >> 4) The client calls another extern "C" function which returns the shared lib's std::string back to the client (again using std::string::c_str() ).

    c_str() returns the null terminated char array which is different than returning a std::string object.
    Yes, I know, but I can't return an std::string using an extern function (I'm carrying about the value not the actuall object). So what are you trying to tell me? Is that the problem? And why?

  6. #6
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Shared Lib: Can const char * travel between client and shared libs?

    It depends on what you are doing with returned c-style string. Are you creating another std::string object? Are these functions accessed by different threads? Do you need to synchronize access.

    I don't know the issues with modifying the c string of a std::string from another std::string instance. Have you tested this?

  7. #7
    Join Date
    Jan 2015
    Posts
    16

    Re: Shared Lib: Can const char * travel between client and shared libs?

    Are you sure that you're not returning a c_str() from an object that is either a "temporary" or one that goes out of scope?

  8. #8
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Shared Lib: Can const char * travel between client and shared libs?

    Quote Originally Posted by jlb1 View Post
    Are you sure that you're not returning a c_str() from an object that is either a "temporary" or one that goes out of scope?
    Yes. The std::string is a member of windowImpl (inside dll) class which lives as long as the entire application.

    I will try using everywhere const char * instead of std::string, to see if this fixes it.
    Last edited by babaliaris; August 30th, 2019 at 07:47 AM.

  9. #9
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Shared Lib: Can const char * travel between client and shared libs?

    LOL I think I know what is going on. The problem lies definitely in these two:

    windowImpl.h:
    Code:
    #ifndef VAMPENGINE_WINDOWIMPL_H
    #define VAMPENGINE_WINDOWIMPL_H
    
    #include <iostream>
    
    struct GLFWwindow;
    
    namespace VampEngine
    {
        class WindowImpl
        {
            
            public:
           .
           .
           .
            inline std::string getTitle() {return m_Title;}
           .
           .
           .
        };
    }
    
    #endif
    Extern c func inside windowImpl.cpp:
    Code:
    const char *vampWindowGetTitle(void *windowObj)
    {
        VampEngine::WindowImpl *self = (VampEngine::WindowImpl *)windowObj;
    
        return self->getTitle().c_str();
    }
    It seems that because the extern function vampWindowGetTitle(void *windowObj) is outside of the window's object scope, it has problems calling getTitle().c_str(). I changed the inline method to return a const char * and now it works.

    I beleive that the reason that the string travelled correctly to the dll, was because the std::string.c_str() was called inside the parameters of a factory extern "C" function so the value of the string was actually getting copied by the copy constructor.

    I solved the problem by changing the inline getTitle() method to return a const char * instead of std::string:
    windowImpl.h:
    Code:
    namespace VampEngine
    {
        class WindowImpl
        {
            
            public:
           .
           .
           .
            inline const char *getTitle() {return m_Title.c_str();}
           .
           .
           .
        };
    }
    
    #endif
    But how does this, return a copy of the m_TItle and the extern function could not return a copy? Is it because I'm calling c_str() from inside the window's object scope? And if yes why?
    Last edited by babaliaris; August 30th, 2019 at 08:34 AM.

  10. #10
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Shared Lib: Can const char * travel between client and shared libs?

    Well after some thoughts I think I know the answer now. Is it because the inline std::string WindowImpl::getTitle() returns a copy of the std::string object right? So inside here:

    extern "C" const char *vampWindowGetTitle(void *windowObj)
    Code:
    const char *vampWindowGetTitle(void *windowObj)
    {
        VampEngine::WindowImpl *self = (VampEngine::WindowImpl *)windowObj;
    
        return self->getTitle().c_str();
    }
    self->getTitle() is a copy std::string which lives in the scope of this extern "C" function. So, after the execution goes out of scope, the std::string gets deleted.

  11. #11
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: Shared Lib: Can const char * travel between client and shared libs?

    You need to be carefui with the allocated memory of STL containers etc. Whilst this is ok:

    Code:
    auto retstr()
    {
        auto test = "qwerty"s;
        return test;
    }
    
    auto ret {retstr()};
    This is not:

    Code:
    auto retptr()
    {
        auto test = "qwerty"s;
        return test.c_str();
    }
    
    auto ret {retstr()};
    In the first, string is returned effectively by move semantics so ret is also a string with valid content.

    In the second, the memory address used by string in the function is returned. However, this memory address is only valid for the scope of the string test in the function retptr(). As soon as retptr() exits, the destructor for test is called which frees the memory used (for this purpose forget about SBO). However, ret is now set to point to this already freed memory - hence a problem. When returning a pointer, the memory referenced has to be valid for the duration that the returned pointer memory is used.
    Last edited by 2kaud; August 30th, 2019 at 11:18 AM.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  12. #12
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Shared Lib: Can const char * travel between client and shared libs?

    Quote Originally Posted by 2kaud View Post
    You need to be carefui with the allocated memory of STL containers etc. Whilst this is ok:

    Code:
    auto retstr()
    {
        auto test = "qwerty"s;
        return test;
    }
    
    auto ret {retstr()};
    This is not:

    Code:
    auto retptr()
    {
        auto test = "qwerty"s;
        return test.c_str();
    }
    
    auto ret {retstr()};
    In the first, string is returned effectively by move semantics so ret is also a string with valid content.

    In the second, the memory address used by string in the function is returned. However, this memory address is only valid for the scope of the string test in the function retptr(). As soon as retptr() exits, the destructor for test is called which frees the memory used (for this purpose forget about SBO). However, ret is now set to point to this already freed memory - hence a problem. When returning a pointer, the memory referenced has to be valid for the duration that the returned pointer memory is used.
    Yeap, I get it know!!! Well my mind probably was thinking that c_str() returns a copy of the actuall string, but this is not the case.
    Thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured