Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding duplicates in array at compile time

I am trying to learn some more modern C++ practices such as templates, and I decided to create a naive and simple command line argument parser that mostly works at compile time and I am already running into issues with constexpr, essentially all I want to do is check for duplicate entries at compile time (doing it at run time is trivial).

First, I have a structure that holds a single configuration:

struct Arg_Opt_Tuple {
  std::string_view mc{}; // multichar ie "help" 
  char sc{}; // singlechar ie 'h' 
  bool is_flag{}; 
};

Now let's say I wanted to create a function (or eventually a constructor to an object) that returns a fixed size std::array, but also does some checking at compile time for duplicates or empty values, my goal is to have it called in some fashion similar to this:

constexpr auto ARG_COUNT = 4U;
constexpr auto opts = checked_arr<ARG_COUNT>(
  Arg_Opt_Tuple{"hello", 'h', false},
  Arg_Opt_Tuple{"world", 'g', true},
  Arg_Opt_Tuple{"goodbye", 'h', false}, // <- static_assert('h' == 'h')
  Arg_Opt_Tuple{"hello", 'r', false} // <- static_assert(sv.compare("hello") == 0)
);

My first attempt was to use a std::initializer_list but ran into some issues and after doing some googling came to the conclusion it's not the correct thing to do here in conjunction with constexpr. My current attempt involves a variadic template:

template <std::size_t N, typename... T>
constexpr std::array<Arg_Opt_Tuple, N> checked_arr(T... list) {
  static_assert(N == sizeof...(T));
  return {list...};
}

This works but is completely superfluous to just initalizing an array, I really want this to be doing some compile time checking. For duplicates or erroneous values at run time is easy, you can just loop through and compare or do std::find or what not, however none of this seems to work at compile time, ie (I know it's ugly but you get the point):

for (std::size_t src_i = 0; src_i < ARG_COUNT; ++src_i) {
  for (std::size_t check_i = 0; check_i < ARG_COUNT; ++check_i) {
    // skip checking self
    if (check_i == src_i) {
      continue;
    }
    // doesnt work obviously
    static_assert(opts[src_i].sc != opts[check_i].sc);
  }
}

So how difficult would this be to achieve? Is this bad design? Any pointers would be lovely.

like image 583
Tom Lulz Avatar asked Jan 19 '19 22:01

Tom Lulz


People also ask

How do you find duplicates in an array?

Duplicate elements can be found using two loops. The outer loop will iterate through the array from 0 to length of the array. The outer loop will select an element. The inner loop will be used to compare the selected element with the rest of the elements of the array.

How do you find duplicates in an array java?

One more way to detect duplication in the java array is adding every element of the array into HashSet which is a Set implementation. Since the add(Object obj) method of Set returns false if Set already contains an element to be added, it can be used to find out if the array contains duplicates in Java or not.

How many duplicates are in an array?

Given an array of integers, count the number of duplicate array elements. Duplicate is defined as more than one identical elements. For example, in the array [1, 3, 3, 5, 5, 5], the two 3's are one duplicate and so are the three 5's. So, the number of duplicates is 2.


1 Answers

For duplicates or erroneous values at run time is easy, you can just loop through and compare or do std::find or what not, however none of this seems to work at compile time

Plain loops do work:

template <typename T> constexpr bool has_duplicates(const T *array, std::size_t size)
{
    for (std::size_t i = 1; i < size; i++)
        for (std::size_t j = 0; j < i; j++)
            if (array[i] == array[j])
                return 1;
    return 0;
}

constexpr int foo[] = {1, 2, 3, 4};
static_assert(!has_duplicates(foo, 4));

If you want to have static_assert inside of a function, you need to pass the array as a template parameter instead:

template <auto &array> constexpr void assert_has_no_duplicates()
{
    constexpr std::size_t size = std::extent_v<std::remove_reference_t<decltype(array)>>;
    static_assert(!has_duplicates(array, size));
}

constexpr int foo[] = {1, 2, 3, 4};

int main()
{
    assert_has_no_duplicates<foo>();
}

Or, if you prefer std::arrays:

template <auto &array> constexpr void assert_has_no_duplicates()
{
    static_assert(!has_duplicates(array.data(), array.size()));
}

constexpr std::array<int,4> foo = {1, 2, 3, 4};

int main()
{
    assert_has_no_duplicates<foo>();
}
like image 151
HolyBlackCat Avatar answered Oct 19 '22 18:10

HolyBlackCat