container_traits icon indicating copy to clipboard operation
container_traits copied to clipboard

Container Traits for Modern C++

This library provides compile-time utilities for STL containers.

#include "container_traits.h"
using namespace container_traits;
#include <iostream>

int main() {
  std::cout << std::boolalpha;

  // has_{member_function}<Container, MemberFunctionSignature>::value
  
  std::cout << has_push_back<std::vector<int>, void(int&)>::value << "\n";       // true
  std::cout << has_push_back<std::queue<int>, void(const int&)>::value << "\n";  // false

  std::cout << has_push_front<std::vector<int>, void(const int&)>::value << "\n";  // false
  std::cout << has_push_front<std::deque<int>, void(const int&)>::value << "\n";   // true

  std::cout << has_subscript_operator<std::vector<char>, char&(size_t)>::value << "\n"; // true
  std::cout << has_subscript_operator<std::list<char>, char&(size_t)>::value << "\n";   // false

  std::cout << has_at<std::map<int, std::string>, std::string&(const int&)>::value << "\n"; // true

  std::cout << has_find<std::string, size_t(const std::string&, size_t)>::value << "\n"; // true

  std::cout << has_data<std::vector<int>, int*()>::value << "\n";   // true
  std::cout << has_data<std::array<int, 3>, int*()>::value << "\n"; // true
  std::cout << has_data<std::list<int>, int*()>::value << "\n";     // false

  std::cout << array_size<std::array<int, 6>>::value << "\n"; // 6

}

This enables the ability to write functions like push_back_or_insert like below.

template <typename Container, typename ValueType>
void push_back_or_insert(Container &c, ValueType &&value) {
  if constexpr (has_push_back<Container, void(const ValueType &)>::value) {
    // has `push_back`
    c.push_back(std::forward<ValueType>(value));
  } 
  else if constexpr (has_push<Container, void(const ValueType&)>::value) {
    // has `push`
    c.push(std::forward<ValueType>(value));
  }
  else if constexpr (has_insert<Container,
                                  typename Container::iterator(typename Container::iterator,
                                                               const ValueType &)>::value &&
                       has_end<Container, typename Container::iterator()>::value) {
    // has `insert` and `end`
    c.insert(c.end(), std::forward<ValueType>(value));
  }
}

int main() {
  auto vec = std::vector<int>{1, 2, 3, 4, 5};
  push_back_or_insert(vec, 6);

  auto map = std::map<int, std::string>{{1, "a"}, {2, "b"}, {3, "c"}};
  push_back_or_insert(map, std::make_pair(4, "d"));

  auto deque = std::deque<float>{1.1f, 2.2f, 3.3f};
  push_back_or_insert(deque, 4.4f);

  auto queue = std::queue<int>{};
  push_back_or_insert(queue, 1);

  auto priority_queue = std::priority_queue<int>{};
  push_back_or_insert(priority_queue, 1);
}