EliasFarhan/NekoEngine

Erreur de compilation pour FixedMap templatisée [Oleg].

Closed this issue · 3 comments

Je suis en train de refactoriser la classe FixedMap selon les feedbacks d'hier. J'ai créé un google test pour la tester.
Lorsque je lance la compilation, j'ai cette erreur:

/usr/bin/ld: CMakeFiles/Neko_TEST.dir/test_map.cpp.o: in function `neko::Engine_FixedMap_Test::TestBody()':
/home/user/Desktop/NekoEngine/test/test_map.cpp:13: undefined reference to `neko::FixedMap<unsigned int, float, 512ul>::FixedMap()'

J'ai l'impression qu'il n'arrive pas à instancier la classe templatisée.

Voici le test_map.cpp:

#include <gtest/gtest.h>
#include <mathematics/map.h>
#include <vector>
#include <algorithm>

namespace neko
{

TEST(Engine, FixedMap)
{
    const size_t MAP_SIZE = 512;

    **ERREUR ICI ->** FixedMap<unsigned int, float, MAP_SIZE> map = FixedMap<unsigned int, float, MAP_SIZE>();
    float expectedSum = 0;
    float sum = 0;

    std::array<unsigned int, MAP_SIZE> keys{0};
    std::array<float, MAP_SIZE> values{0};
    for (size_t i = 0; i < MAP_SIZE; ++i)
    {
        keys[i] = i;
        values[i] = i;
        expectedSum += i;
    }
    for (size_t i = 0; i < MAP_SIZE; ++i)
    {
        map.Append(keys[i], values[i]);
    }

    // [] testing.
    for (size_t i = 0; i < MAP_SIZE; ++i)
    {
        sum += map[i];
    }
    EXPECT_EQ(expectedSum, sum);
    sum = 0;

    std::for_each(values.begin(), values.end(), [](float& f) { f = -f; }); // Negate all values.
    for (size_t i = 0; i < MAP_SIZE; ++i)
    {
        map[i] = values[i];
    }
    for (size_t i = 0; i < MAP_SIZE; ++i)
    {
        sum -= values[i];
    }
    EXPECT_EQ(expectedSum, sum);
    sum = 0;

    // Iterators testing.
    std::for_each(map.begin(), map.end(), [](std::pair<xxh::hash_t<64>, float>& p) {
        p.first = 0;
        p.second = 0;
    });
    std::for_each(map.begin(), map.end(), [&sum](std::pair<xxh::hash_t<64>, float>& p) { sum += p.second; });
    EXPECT_EQ(0, sum);
}

}// !neko

Le header:

#pragma once

#include <engine/custom_allocator.h>
#include <vector> // TODO: replace this with neko's own container.
#include <xxhash.hpp>

namespace neko
{

template<typename Key, typename Value, const size_t Size>
class FixedMap
{
public:
    using Hash = xxh::hash_t<64>;
    using Pair = std::pair<Hash, Value>;

    **CONSTRUCTEUR PAS APPELÉ** -> FixedMap();

    ~FixedMap();

    [[nodiscard]] size_t Capacity() const;

    Value& operator[](Key key);

    bool Contains(Key key) const;

    void Append(Key key, Value value);

    typename std::vector<Pair>::iterator begin();

    typename std::vector<Pair>::iterator end();

    typename std::vector<Pair>::const_iterator cbegin() const;

    typename std::vector<Pair>::const_iterator cend() const;

private:
    std::vector<Pair> pairs_; // TODO: Replace this with neko's container when it's ready.
    // Allocator& allocator_;
};
}// !neko

Et le cpp:

#include <mathematics/map.h>
#include <algorithm>

namespace neko
{
template<typename Key, typename Value, const size_t Size>
FixedMap<Key, Value, Size>::FixedMap(/*Allocator&& allocator*/)
{
    // allocator_ = allocator; // TODO: Use the allocator when neko vectors are implemented.
    pairs_.resize(Size); // Initializes the pairs to 0.
}

template<typename Key, typename Value, const size_t Size>
FixedMap<Key, Value, Size>::~FixedMap()
{
    std::for_each(pairs_.begin(), pairs_.end(), [](Pair& p) { p.first = 0; });
}

template<typename Key, typename Value, const size_t Size>
size_t FixedMap<Key, Value, Size>::Capacity() const
{
    return pairs_.capacity();
}

template<typename Key, typename Value, const size_t Size>
Value& FixedMap<Key, Value, Size>::operator[](const Key key)
{
    const Hash hash = xxh::xxhash<64>(&key, sizeof(Key));
    const auto it = std::find_if(pairs_.begin(), pairs_.end(), [](Pair& p) { return p.first == hash; });

    neko_assert(it == pairs_.end(),
                "neko::Map<Key,Value>::operator[](const Key): Key passed to operator not found.");
    return *it;
}

template<typename Key, typename Value, const size_t Size>
bool FixedMap<Key, Value, Size>::Contains(const Key key) const
{
    const Hash hash = xxh::xxhash<64>(&key, sizeof(Key));
    return std::find_if(pairs_.begin(), pairs_.end(), [](Pair p) { return p.first == hash; }) != pairs_.end();
}

template<typename Key, typename Value, const size_t Size>
void FixedMap<Key, Value, Size>::Append(const Key key, const Value value)
{
    neko_assert(Contains(key),
                "neko::Map<Key,Value>::Append(const Key, const Value): Map already contains Key passed.")
    const auto it = std::find_if(pairs_.begin(), pairs_.end(), [](Pair& p) { return p.first == 0; });
    neko_assert(it == pairs_.end(),
                "neko::Map<Key,Value>::Append(const Key, const Value): No more free slots in map.")
    it->first = xxh::xxhash<64>(&key, sizeof(Key));
    it->second = value;
}

template<typename Key, typename Value, const size_t Size>
typename std::vector<std::pair<xxh::hash_t<64>, Value>>::iterator FixedMap<Key, Value, Size>::begin()
{
    return pairs_.begin();
}


template<typename Key, typename Value, const size_t Size>
typename std::vector<std::pair<xxh::hash_t<64>, Value>>::iterator FixedMap<Key, Value, Size>::end()
{
    return pairs_.end();
}

template<typename Key, typename Value, const size_t Size>
typename std::vector<std::pair<xxh::hash_t<64>, Value>>::const_iterator FixedMap<Key, Value, Size>::cbegin() const
{
    return pairs_.cbegin();
}

template<typename Key, typename Value, const size_t Size>
typename std::vector<std::pair<xxh::hash_t<64>, Value>>::const_iterator FixedMap<Key, Value, Size>::cend() const
{
    return pairs_.cend();
}

}// !neko

Si vous avez du feedback sur la manière d'implémenter les fonctions d'iterators d'ailleurs c'est aussi le bienvenu ( begin(), end(), cbegin(), cend() ) .

Update: C'est pcq le .cpp est pas inclu lors de la compilation. J'ai essayé de remettre la definition du constructeur dans le header et l'erreur disparait.
J'ai créé un map.cpp dans src/mathematics/map.cpp (mathematics est un nouveau dossier).

Edit #1: Dans src/CMakeLists.txt on a bien la ligne:

file(GLOB_RECURSE Neko_MATH_SRC src/mathematics/*.cpp include/mathematics/*.h)

Donc ça deverait faire le link avec le cpp non?
J'ai bien vérifié que le nom du dossier est identique au celui dans src/CMakeLists.txt

Voilà un screenshot des fichiers:
Screenshot from 2020-03-12 08-20-10

Edit #2:
Je crois qu'il arrive bien à include le .cpp mais il comprends pas que ce qui se trouve dans map.cpp est une définition de la déclaration qui est dans map.h.
Il croit que c'est une fonction statique, ya un pb avec la manière dont j'ai déclaré l'implémentation des fonctions...

Je crois que j'ai trouvé ma réponse... je peux pas séparer ça entre un .h et un .cpp:
https://stackoverflow.com/questions/8752837/undefined-reference-to-template-class-constructor

C'est ça :)