0

I have a CMake project with the following hierarchy:

- project
  - include
    - project
      - Allocator.h
      - LinearAllocator.h
      - StackAllocator.h
  - src
    - CMakeLists.txt
    - LinearAllocator.cpp
    - StackAllocator.cpp
  - sandbox
    - CMakeLists.txt
    - main.cpp
  - CMakeLists.txt

Main CMakeLists.txt:

cmake_minimum_required(VERSION 3.8.1)
project(project
    VERSION 1.0.0
    LANGUAGES CXX)

add_subdirectory(src)
add_subdirectory(sandbox)

src folder CMakeLists.txt:

file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../include/project/*.h")

add_library(project STATIC ${SOURCES})

target_include_directories(project PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")

sandbox folder CMakeLists.txt:

add_executable(sandbox main.cpp)

target_link_libraries(sandbox PRIVATE project)

sandbox main.cpp:

#include "project/Allocator.h"
#include "project/LinearAllocator.h"
#include "project/StackAllocator.h"

#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <cstddef>

struct L
{
    char m_a;
    char m_b;
    char m_c;

    L(char a, char b, char c)
        : m_a(a), m_b(b), m_c(c)
    {}

    L(const L& other)
    {
        m_a = other.m_a;
        m_b = other.m_b;
        m_c = other.m_c;
        std::cout << "Copy L()\n";
    }

    ~L()
    {
        std::cout << "~L()\n";
    }
};

struct M
{
    char m_a;
    int m_i;

    M(char a, int i)
        : m_a(a), m_i(i)
    {}

    M(const M& other)
    {
        m_a = other.m_a;
        m_i = other.m_i;
        std::cout << "Copy M()\n";
    }

    ~M()
    {
        std::cout << "~M()\n";
    }
};

template<typename T, typename Alloc, typename... Args>
std::unique_ptr<T[], std::function<void(T*)>> make_T_Construct(Alloc& alloc, std::size_t size, Args&&... args)
{
    T* ptr = reinterpret_cast<T*>(alloc.Allocate(size * sizeof(T), alignof(T)));

    for (std::size_t i = 0; i < size; ++i)
    {
        new(ptr + i) T(std::forward<Args>(args)...);
    }

    auto deleter = [](T* p, Alloc& alloc, std::size_t size)
    {
        for (std::size_t i = 0; i < size; ++i)
        {
            p[i].~T();
        }
        //alloc.Deallocate(p, sizeof(T) * size);
    };

    return { ptr, std::bind(deleter, std::placeholders::_1, std::ref(alloc), size) };
}

int main()
{
    std::cout << "Alignment of L: " << alignof(L) << '\n';
    std::cout << "Size of L: " << sizeof(L) << '\n';
    std::cout << "Size of L*: " << sizeof(L*) << '\n';
    std::cout << "Alignment of M: " << alignof(M) << '\n';
    std::cout << "Size of M: " << sizeof(M) << '\n';
    std::cout << "Size of M*: " << sizeof(M*) << '\n';
    {
        auto alloc = project::LinearAllocator(80);
        auto ptr0 = make_T_Construct<L>(alloc, 2, 'a', 'b', 'c');
        auto ptr1 = make_T_Construct<char>(alloc, 1, 'Z');
        auto ptr2 = make_T_Construct<M>(alloc, 1, 'D', 5);
        std::cout << "ptr0[0].m_a: " << ptr0.get()[0].m_a << '\n';
        std::cout << "ptr0[1].m_a: " << ptr0.get()[1].m_a << '\n';
        std::cout << "ptr1[0]: " << ptr1.get()[0] << '\n';
        std::cout << "ptr2[0].m_a: " << ptr2.get()[0].m_a << '\n';
        std::cout << "ptr2[0].m_i: " << ptr2.get()[0].m_i << '\n';
        std::cout << "Fragmentation: " << alloc.Fragmentation() << '\n';
    }
    return 0;
}

This compiles and links fine, but the moment I add the StackAllocator:

    {
        auto alloc = project::StackAllocator(80);
        auto ptr0 = make_T_Construct<L>(alloc, 2, 'a', 'b', 'c');
        auto ptr1 = make_T_Construct<char>(alloc, 1, 'Z');
        auto ptr2 = make_T_Construct<M>(alloc, 1, 'D', 5);
        std::cout << "ptr0[0].m_a: " << ptr0.get()[0].m_a << '\n';
        std::cout << "ptr0[1].m_a: " << ptr0.get()[1].m_a << '\n';
        std::cout << "ptr1[0]: " << ptr1.get()[0] << '\n';
        std::cout << "ptr2[0].m_a: " << ptr2.get()[0].m_a << '\n';
        std::cout << "ptr2[0].m_i: " << ptr2.get()[0].m_i << '\n';
        std::cout << "Fragmentation: " << alloc.Fragmentation() << '\n';
    }

I get linking errors for unresolved external symbols for StackAllocator methods.
Any ideas why ?

Jorayen
  • 1,737
  • 2
  • 21
  • 52
  • Be very careful when using `GLOB_RECURSE` to grab source files. This is evaluated at *generation time* not at *build time*. Try re-running CMake first and foremost. – Botje May 14 '20 at 11:06
  • @Botje Already tried rerunning cmake – Jorayen May 14 '20 at 11:21
  • Next step: are the missing symbols present in StackAllocator.cpp and is it getting compiled? Show errors and compile output after touching StackAllocator.cpp. – Botje May 14 '20 at 11:23
  • Yea the library project getting build successfully and all symbols are present – Jorayen May 14 '20 at 11:27
  • Your linker seems to disagree, so please show linker errors, full linker command line, and proof (via `dumpbin.exe` or `nm`) that these symbols are present in the static library. – Botje May 14 '20 at 11:29
  • @Botje I've noticed that `cl.exe` have built the lib without `StackAllocator` indeed. Wondered why and printed `SOURCES` like so: `message(STATUS "SOURCES: ${SOURCES}")` inside `CMakeLists.txt` inside `src` directory, then the build was started using `StackAllocator` again, not sure why maybe it has to do something with `GLOB_RECURSE` as you've said? But then again why rerunning cmake doesn't solved it? Also how would I grab all source and include files in a more safe fashion then? – Jorayen May 14 '20 at 12:45
  • There is no way to automatically grab all sources. CMake best practice is to explicitly list your source files in the CMakeLists.txt. See the warning in the [documentation](https://cmake.org/cmake/help/latest/command/file.html#filesystem). I see they added a `CONFIGURE_DEPENDS` flag to aid, but they make no promises. – Botje May 14 '20 at 12:50
  • @Botje Why did rerunning cmake didn't work then? And you can't be expected to list all files explicitly every time new file is added, it's tedious – Jorayen May 14 '20 at 15:02
  • "Why did rerunning cmake didn't work then?" It `cmake` is rerun, them `GLOB_RECURSE` searches the files again. If it doesn't find a file, then the file isn't here (read: something wrong with your files structure and it doesn't corresponds to the one you show us). If you want to know more about why GLOB_RECURSE is not recommended, then see that question: https://stackoverflow.com/questions/32411963/why-is-cmake-file-glob-evil. – Tsyvarev May 14 '20 at 15:34

0 Answers0