0
votes

I want to implement custom "at" method, which gives more informative exception if some key does not exists in some container (in particular vector/map/deque/unordered_map). However, in my project i have to do some casting of key types (i.e passing int variable as a key to std::map<uint16_t, some_type>) and there is too much cases like this, so i can't modify client code in order to avoid it. And project settings set to trait all warnings as errors, so this cast fails my build because of possible loss of data. To avoid it, i need to cast passed key.

I wrote this:

template <typename CntT, typename KeyT>
auto at(const CntT& container, const KeyT& key) -> decltype(container.at(key))
{
    using CKeyType = ContainerTraits<container>::KeyType;

    const auto safeKey    = reinterpret_cast<CKeyType>(key);
    const bool valueExist = IsExists(container, safeKey);

    if (!valueExists)
    {
        // generate some exception
    }

    return container.at(safeKey);
}

The problem here is ContainerTraits structure, because i need ability to pass container with ANY template parameters (the project use set of custom allocators etc.), so i wrote this:

template <typename CntT>
struct ContainerTraits;

template <typename ValT,
          typename AllocT>
struct ContainerTraits<std::vector<ValT, AllocT>>
{
    using KeyType = typename std::vector<ValT, AllocT>::size_type;
    using ValType = typename std::vector<ValT, AllocT>::value_type;
};

template <typename KeyT,
          typename ValT,
          typename HashT,
          typename KeyEqT,
          typename AllocT>
struct ContainerTraits<std::unordered_map<KeyT, ValT, HashT, KeyEqT, AllocT>>
{
    using KeyType = typename std::unordered_map<KeyT, ValT, HashT, KeyEqT, AllocT>::key_type;
    using ValType = typename std::unordered_map<KeyT, ValT, HashT, KeyEqT, AllocT>::mapped_type;
};

But this doe not compile. So how can i specialize some template struct to pass for example std::map with any template arguments?

IsExists implementation:

template <typename ValT,
          typename AllocT>
bool IsExistsImpl(const std::vector<ValT, AllocT>&              vec,
                  const typename std::vector<ValT>::size_type&  key)
{
    return key < vec.size();
}

template <typename KeyT,
          typename ValT,
          typename HashT,
          typename KeyEqT,
          typename AllocT>
bool IsExistsImpl(const std::unordered_map<KeyT, ValT, HashT, KeyEqT, AllocT>& map, const KeyT& key)
{
    return map.find(key) != map.end();
}

template <typename CntT, typename KeyT>
bool IsExists(const CntT& container, const KeyT& key)
{
    return IsExistsImpl(container, key);
}
1
What is IsExists? minimal reproducible example please. - Paul Sanders
Sorry, forgot it. Updated code - Timur Yalimov
"But this doe not compile" What's the error message? Anyway, see if this helps: template <typename... Args> struct ContainerTraits<std::vector<Args...>> {...}; This should accept any vector specialization. Other containers should work the same way - you won't need to spell out all parameters for every container. - Igor Tandetnik

1 Answers

1
votes

using CKeyType = ContainerTraits<container>::KeyType;

should instead be:

using CKeyType = ContainerTraits<CntT>::KeyType;

Also (typo):

const bool valueExist = IsExists(container, safeKey);

should be:

const bool valueExists = IsExists(container, safeKey);

But the most problematic line is this one:

const auto safeKey = reinterpret_cast<CKeyType>(key);

since:

  • CKeyType is not const
  • the cast fails if CKeyType is not a primitive type

The best replacement seems to be:

const auto& safeKey = static_cast<const CKeyType &>(key);

although the restrictions of static_cast may not suit your use case. reinterpret_cast is too dangerous here.

Live demo