Странная неупорядоченная ситуация на карте - PullRequest
3 голосов
/ 27 ноября 2011

У меня есть общая библиотека с классом:

// HPP:
class WorldSettings
{
    private:
        static std::unordered_map<std::string, int> mIntegerStorage;
        static std::unordered_map<std::string, float> mFloatStorage;
        static std::unordered_map<std::string, std::string> mStringStorage;

    public:
        template <typename T>
        static T &Get(const std::string &key);

        template <typename T>
        static T &Set(const std::string &key, T value);
};

// CPP:

#define DoReturn(MapName, Key, Type) {                                                \
    assert( MapName.find(Key) != MapName.end() && "No such key in world settings!" ); \
    return MapName[Key];                                                              \
}                                                                                     \

#define DoSetup(MapName, Key, Value) { \
    MapName[key] = Value;              \
    return MapName[Key];               \
}                                      \

std::unordered_map<std::string, int> WorldSettings::mIntegerStorage;
std::unordered_map<std::string, float> WorldSettings::mFloatStorage;
std::unordered_map<std::string, std::string> WorldSettings::mStringStorage;

// Getters

template <>
int &WorldSettings::Get<int>(const std::string &key)
{
    DoReturn(mIntegerStorage, key, int);
}

template <>
float &WorldSettings::Get<float>(const std::string &key)
{
    DoReturn(mFloatStorage, key, float);
}

template <>
std::string &WorldSettings::Get<std::string>(const std::string &key)
{
    DoReturn(mStringStorage, key, std::string);
}

// Setters

template <>
int &WorldSettings::Set<int>(const std::string &key, int value)
{
    DoSetup(mIntegerStorage, key, value);
}

template <>
float &WorldSettings::Set<float>(const std::string &key, float value)
{
    DoSetup(mFloatStorage, key, value);
}

template <>
std::string &WorldSettings::Set<std::string>(const std::string &key, std::string value)
{
    DoSetup(mStringStorage, key, value);
}

Теперь я хочу использовать этот класс в не общей библиотеке (простое консольное приложение):

WorldSettings::Get<int>("WorldMinutes");

«WorldMinutes» установлен в коде общей библиотеки:

WorldSettings::Set<int>("WorldMinutes", 0);

Проблема:

Исключение с плавающей точкой

Program received signal SIGFPE, Arithmetic exception.
0x00000000004d1f61 in std::__detail::_Mod_range_hashing::operator() (this=0x747863, __num=732984944481197501, 
    __den=0) at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../include/c++/4.6.2/bits/hashtable_policy.h:376
376     { return __num % __den; }

Backtrace:

#0  0x00000000004d1f61 in std::__detail::_Mod_range_hashing::operator() (this=0x747863, __num=732984944481197501, 
    __den=0) at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../include/c++/4.6.2/bits/hashtable_policy.h:376
#1  0x00000000004d3503 in std::__detail::_Hash_code_base<std::string, std::pair<std::string const, int>, std::_Select1st<std::pair<std::string const, int> >, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_bucket_index (this=0x747860, __c=732984944481197501, __n=0)
    at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../include/c++/4.6.2/bits/hashtable_policy.h:758
#2  0x00000000004d2a9a in std::__detail::_Map_base<std::string, std::pair<std::string const, int>, std::_Select1st<std::pair<std::string const, int> >, true, std::_Hashtable<std::string, std::pair<std::string const, int>, std::allocator<std::pair<std::string const, int> >, std::_Select1st<std::pair<std::string const, int> >, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true> >::operator[] (this=0x747860, __k=...)
    at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../include/c++/4.6.2/bits/hashtable_policy.h:543
#3  0x00000000004d1cfa in WorldSettings::Set<int> (key=..., value=0)

Ошибка вызывается, когда Set<int>(...) в коде разделяемой библиотеки. Интересно то, что у меня нет ошибки при вызове Get из кода библиотеки. Что здесь может быть не так?

Ответы [ 2 ]

3 голосов
/ 27 ноября 2011

Код неверен как есть.

Специализации, которые вы определяете в файле .cpp, должны были быть объявлены (вне класса) в файле .hpp, в противном случае каждый раз, когда вы пытаетесь использовать определенную функцию, вы получаете неопределенное поведение.

Добавить:

template <>
int &WorldSettings::Get<int>(const std::string &key);

template <>
float &WorldSettings::Get<float>(const std::string &key);

template <>
std::string &WorldSettings::Get<std::string>(const std::string &key);

// Setters

template <>
int &WorldSettings::Set<int>(const std::string &key, int value);

template <>
float &WorldSettings::Set<float>(const std::string &key, float value);

template <>
std::string &WorldSettings::Set<std::string>(const std::string &key, std::string value);

сразу после определения класса.

Это говорит компилятору, что определения генерируются в другом месте.

В действительности, вероятно, что совместно используемая библиотека сгенерирует свою собственную копию функций (вы предоставили некоторую реализацию шаблона по умолчанию?), И это может вызвать хаос.

Возможно, это не является причиной ошибки, но нет смысла продолжать расследование.

2 голосов
/ 27 ноября 2011

Это не ответ, а настоятельно рекомендуемое предложение по улучшению: избавьтесь от этих макросов и используйте шаблоны C ++:

template <typename Map>
typename Map::mapped_type const & cget(Map const & m, typename Map::key_type const & k)
{
  typename Map::const_iterator it = m.find(k);
  assert(it != m.cend());
  return it->second;
}

template <typename Map>
typename Map::mapped_type & get(Map & m, typename Map::key_type const & k)
{
  typename Map::iterator it = m.find(k);
  assert(it != m.end());
  return it->second;
}

template <typename Map>
typename Map::mapped_type & put(Map & m, typename Map::key_type const & k, typename Map::mapped_type const & v)
{
  std::pair<typename Map::iterator, bool> p = m.insert(typename Map::value_type(k, v));
  return p.first->second;
}

Теперь вы можете сказать:

template <>
int & WorldSettings::Get<int>(const std::string & key)
{
  get(mIntegerStorage, key);
}

template <>
float & WorldSettings::Set<float>(const std::string & key, float value)
{
  put(mFloatStorage, key, value);
}

Вы даже можете переместить специализации в контейнер : получатель / установщик становится, как правило:

template <typename T> static T & Get(const std::string & key)
{
  return get(Storage<T>::container, key);
}
template <typename T> static T & Set(const std::string & key, T value)
{
  return put(Storage<T>::container, key, value);
}

Вам просто нужен вложенный класс:

template <typename T> struct Storage
{
  static std::unordered_map<std::string, T> container;
};

Теперь все, что вам нужно, это предоставить конкретные экземпляры объектов для контейнеров, которые вы хотите использовать:

template <>
std::unordered_map<std::string, int> WorldSettings::Storage<int>::container{};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...