2

I currently have a function that generate the type name for an array type. It's currently using other code that already runs at compile time. For example, for a variable such as int data[4], the function returns the string int[4]:

template<typename Class, int Size>
constexpr auto getName(Class (&)[Size])
{
    // code that already runs at compile time:
    constexpr auto name = getName<Class>();
    constexpr auto length = getNumericString<Size>();
    constexpr auto size = getStrLen(name) + getStrLen(length) + 3;

    // code I would like to run at compile time:
    static char buffer[size] = {0};
    if (buffer[0] == 0) {
        auto i = 0, j = 0;
        while (name[j] != 0) {
            buffer[i++] = name[j++];
        }
        buffer[i++] = '[';
        j = 0;
        while (length[j] != 0) {
            buffer[i++] = length[j++];
        }
        buffer[i++] = ']';
    }
    return buffer;
}

Is it possible to write the bottom part of that function to run at compile time somehow? It's just putting two const char* together with the [ and ] characters to denote the array size. And if its possible, how?

Thanks!

7
  • Technically possible? Yes. Templates are Turing-complete. Read: Template Metaprogramming. But should you? Commented Aug 9, 2018 at 18:38
  • 1
    Possible duplicate: stackoverflow.com/questions/38955940/… Commented Aug 9, 2018 at 18:43
  • @IvanRubinson I'll use 'em everywhere I can. Commented Aug 9, 2018 at 19:00
  • As long as it's limited to Code Golf and not used in production, I can live with that. There can be many issues from abusing ones programming language, especially when working in a team. Commented Aug 9, 2018 at 19:03
  • With std::array<char, size>, it would be easier... Commented Aug 9, 2018 at 19:09

1 Answer 1

2

Yes, it is possible. The most tricky part is that you ask for a solution which returns a const char* (rather than something which owns the data).

As you notice in your example, the const char* needs a buffer to point to. This is tricky because constexpr functions are currently (C++17) not allowed to have a static constexpr char buffer[size]{/* whatever*/};. Instead, you can use a static data member of a templated helper class.

The following is a complete demonstration which I have tested with both clang 6.0.1 and GCC 8.1.1 using -std=c++14.

#include <cassert>
#include <cstddef>

#include <iostream>

template<class T>
constexpr const char* getName();

template<>
constexpr const char* getName<int>() {
  return "int";
}

template<std::size_t N>
constexpr const char* getNumericString();

template<>
constexpr const char* getNumericString<16>() {
  return "16";
}

constexpr std::size_t getStrLen(const char* str) {
  std::size_t ret = 0;
  while(str[ret] != '\0') ret++;
  return ret;
}

static_assert(getStrLen("") == 0, "");
static_assert(getStrLen("ab") == 2, "");
static_assert(getStrLen("4\0\0aaa") == 1, "");

struct Wrapper {
  const char* str;

  constexpr auto begin() const { return str; }
  constexpr auto end() const {
    auto it = str;
    while(*it != '\0') ++it;
    return it;
  }
};

template<class T, std::size_t size>
class Array {
 private:
  T data_[size]{};
 public:
  constexpr T& operator[](std::size_t i) { return data_[i]; }
  constexpr const T& operator[](std::size_t i) const { return data_[i]; }
  constexpr const T* data() const { return data_; }
};

template<std::size_t buffer_size, class... Args>
constexpr Array<char, buffer_size> cat(Args... args) {
  Array<char, buffer_size> ret{};

  std::size_t i = 0;
  for(auto arg : {Wrapper{args}...}) {
    for(char c : arg) ret[i++] = c;
  }

  return ret;
}

template<class T, std::size_t N>
struct StaticDataForConstexprFunction {
  static constexpr const char* name = getName<T>();
  static constexpr const char* length = getNumericString<N>();
  static constexpr std::size_t size = getStrLen(name) + getStrLen(length) + 10;

  using Buffer = Array<char, size>;
  static constexpr Buffer buffer = cat<size>(name, "[", length, "]\0");
};

template<class T, std::size_t N>
constexpr typename StaticDataForConstexprFunction<T, N>::Buffer StaticDataForConstexprFunction<T, N>::buffer;

template<class T, std::size_t N>
constexpr const char* getName(T (&)[N]) {
  return StaticDataForConstexprFunction<T, N>::buffer.data();
}

int main() {
  int foobar[16];
  constexpr auto res = getName(foobar);
  std::cout << res << std::endl;
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.