Search code examples
c++classpointersstructimgui

Defining global class/struct pointer between two .cpp files


I have a problem declaring a public/extern struct object between different .cpp files. I am trying to use the imgui logger to log some messages from a hook.

The program is going to crash on ExampleAppLog my_log2; -> ImGuiTextBuffer Buf; -> class ImVector -> if (Data)

Because i do this ExampleAppLog* my_log2 = new ExampleAppLog(); inside a .cpp that have a include .h with the struct ExampleAppLog in it, and a declaration of my_log2 .

Relevant code to crash -> .h

struct ExampleAppLog
    {
        ImGuiTextBuffer     Buf;
    }
extern ExampleAppLog* my_log2;

.cpp

 #include ".h"
 ExampleAppLog* my_log2 = new ExampleAppLog(); //this line make it crash

imgui.h

struct ImGuiTextBuffer
{
    ImVector<char>      Buf;
}

class ImVector
{
public:
    int                         Size;
    int                         Capacity;
    T*                          Data;

    typedef T                   value_type;
    typedef value_type*         iterator;
    typedef const value_type*   const_iterator;

    ImVector()                  { Size = Capacity = 0; Data = NULL; }
    ~ImVector()                 { if (Data) ImGui::MemFree(Data); }

    inline bool                 empty() const                   { return Size == 0; }
    inline int                  size() const                    { return Size; }
    inline int                  capacity() const                { return Capacity; }

    inline value_type&          operator[](int i)               { IM_ASSERT(i < Size); return Data[i]; }
    inline const value_type&    operator[](int i) const         { IM_ASSERT(i < Size); return Data[i]; }

    inline void                 clear()                         { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } }
    inline iterator             begin()                         { return Data; }
    inline const_iterator       begin() const                   { return Data; }
    inline iterator             end()                           { return Data + Size; }
    inline const_iterator       end() const                     { return Data + Size; }
    inline value_type&          front()                         { IM_ASSERT(Size > 0); return Data[0]; }
    inline const value_type&    front() const                   { IM_ASSERT(Size > 0); return Data[0]; }
    inline value_type&          back()                          { IM_ASSERT(Size > 0); return Data[Size-1]; }
    inline const value_type&    back() const                    { IM_ASSERT(Size > 0); return Data[Size-1]; }
    inline void                 swap(ImVector<T>& rhs)          { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }

    inline int                  _grow_capacity(int size) const  { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > size ? new_capacity : size; }

    inline void                 resize(int new_size)            { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
    inline void                 resize(int new_size, const T& v){ if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) Data[n] = v; Size = new_size; }
    inline void                 reserve(int new_capacity)
    {
        if (new_capacity <= Capacity) return;
        T* new_data = (value_type*)ImGui::MemAlloc((size_t)new_capacity * sizeof(T));
        if (Data) //here is the crash. Data is 0x000000000 when crashing
            memcpy(new_data, Data, (size_t)Size * sizeof(T));
        ImGui::MemFree(Data);
};

Exsample code -> .h

struct ExampleAppLog
{
    ImGuiTextBuffer     Buf;
    ImGuiTextFilter     Filter;
    ImVector<int>       LineOffsets;        // Index to lines offset
    bool                ScrollToBottom;

    void    Clear() { Buf.clear(); LineOffsets.clear(); }

    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)
    {
        int old_size = Buf.size();
        va_list args;
        va_start(args, fmt);
        Buf.appendv(fmt, args);
        va_end(args);
        for (int new_size = Buf.size(); old_size < new_size; old_size++)
            if (Buf[old_size] == '\n')
                LineOffsets.push_back(old_size);
        ScrollToBottom = true;
    }

    void    Draw(const char* title, bool* p_open = NULL)
    {
        ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
        ImGui::Begin(title, p_open);
        if (ImGui::Button("Clear")) Clear();
        ImGui::SameLine();
        bool copy = ImGui::Button("Copy");
        ImGui::SameLine();
        Filter.Draw("Filter", -100.0f);
        ImGui::Separator();
        ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
        if (copy) ImGui::LogToClipboard();

        if (Filter.IsActive())
        {
            const char* buf_begin = Buf.begin();
            const char* line = buf_begin;
            for (int line_no = 0; line != NULL; line_no++)
            {
                const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL;
                if (Filter.PassFilter(line, line_end))
                    ImGui::TextUnformatted(line, line_end);
                line = line_end && line_end[1] ? line_end + 1 : NULL;
            }
        }
        else
        {
            ImGui::TextUnformatted(Buf.begin());
        }

        if (ScrollToBottom)
            ImGui::SetScrollHere(1.0f);
        ScrollToBottom = false;
        ImGui::EndChild();
        ImGui::End();
        }
    }; 


extern ExampleAppLog* my_log2;

One.cpp

#include ".h"
        ExampleAppLog* my_log2 = new ExampleAppLog(); //this line make it crash

       void LogHook(const char* Info)
    {
        my_log2->AddLog(Info);
    }

Two.cpp

#include ".h"
    bool bDraw = true;
    void Draw()
    {
      my_log2->Draw("Logger", &bDraw);
    }

I have tried many different methodes but no luck without it ending up crashing when trying to share a extern object in multiple .cpp.

Logger documentation.

static ExampleAppLog my_log; //have tryd this but with extern etc. It still crash at the same place whan trying to share it globaly. If i do it all in one .cpp out sharing it publicly the code work
[...]
my_log.AddLog("Hello %d world\n", 123);
[...]
my_log.Draw("title");

Solution

  • It is hard to tell you what your problem is because important information is missing.

    • Are you sure that it crash while checking if Data is a null pointer?
    • Have you checked if this is valid at the point of crash?
    • Have you put a breakpoint on the constructor to see when it was called.

    While it looks like you don't make any copy of those objects, it would be a good idea to prevent it if not properly supported by deleting copy and move constructor and assignment operators. See https://en.cppreference.com/w/cpp/language/function#Deleted_functions for more information.

    One obvious way to see if the problem is that you call a function of ExampleAppLog before it is created is to put a breakpoint inside the constructor. From the above code, we cannot be sure if the class is created only once or multiple times (from elsewhere).

    Also are you sure that you don't call Draw or LookHook before you create my_log2 object. Again, that kind of thing is trivial to test with a debugger but very hard for us to tell with only part of the code in our hand. In fact, as the above program does not have a main, it is not a MCVE.

    If it really crashes when you are creating ExampleAppLog object and not when trying to use it before it was created, then most of the code above is useless and commenting out code (and remove it from the question) if it still crash, would greatly help people to help you.

    On the other hand, if it crash because you are using my_log2 before it is created, then some required code to reproduce the problem is missing.

    If the problem is related to initialisation order, then a singleton might be the solution. Look at the accepted answer here: How to implement multithread safe singleton in C++11 without using <mutex>.

    In any case, it is hard to help you because you don't put enough effort in your question. Remember that if the code cannot easily be copied and paste, almost nobody will take the time to create a project even more when it is obvious that important lines or information are missing because with the provided information, it is almost impossible that it crash on the specified line.

    In fact, in we assume that main is an empty function and that there are no other global usage of either my_log2 pointer and ExampleAppLog struct, then when would the function reserve be called.

    As a bonus, if you ask good questions, you get more points on the site!