1

I need a class which takes in a compile-time string literal (const char* const) as its only constructor argument.

I would like to have an std::array member variable store the words in this string literal (split up using spaces, for example). The size of this array therefore will never change, as the string literal is known at compile-time.

How can I have this array as a member variable, while having its size be specified by the number of words passed in to the constructor?

I would like to make it so that the caller does not need to specify the number of words in the class template parameters, but that the constructor (presumably a constexpr one) could deduce this at compile time.

I do not want to use a vector, as it is unnecessary for this situation (the words being stored will NEVER change). How would I go about this?

Thanks

Gary Allen
  • 1,218
  • 1
  • 13
  • 28
  • 2
    You need to show what you've done so far and where you're getting an error. – Rohan Bari Jun 11 '20 at 20:36
  • I have not done much as I do not know where to start... I have just been Googling but to no avail. – Gary Allen Jun 11 '20 at 20:36
  • You can't do this. If you don't know the size at class definition time, then you have to use a vector or make the class itself a template that you pass the size to. – NathanOliver Jun 11 '20 at 20:39
  • @NathanOliver could I not somehow have a constexpr function which gets "called" with the string literal as an argument an deduces the number of words, and then passes that along to std::array? – Gary Allen Jun 11 '20 at 20:42
  • gcc/clang has extension to have string literal in UDL using template (so having char_sequence which allows to know word count at compile time). – Jarod42 Jun 11 '20 at 20:45
  • 1
    Not if you want to pass this string into the constructor. In order to define the class itself, like `struct foo { std::array };` you have to know that `some_size` right then. You can't change the size of the class in the constructor so there is no way of passing it to the constructor. – NathanOliver Jun 11 '20 at 20:46
  • if you want something like that, you definitely have to go with the `make_xyz` idiom. Also have a look at `boost::hana::string` https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/structboost_1_1hana_1_1string.html – Sebastian Hoffmann Jun 11 '20 at 20:49
  • 1
    String literals are not `const char*`s; they're `const char[N]`s. – Asteroids With Wings Jun 11 '20 at 20:53
  • Related: [Passing a string literal as a parameter to a C++ template class](https://stackoverflow.com/questions/2033110/) – Remy Lebeau Jun 11 '20 at 21:11

2 Answers2

2

First, you have to use class templates, as there should be a different size of the class for different string literals. Template arguments should at least contain the size of the enclosed array (e.g. template<std::size_t N>).

Then you may find Class Template Argument Deduction from C++17 useful so you don't have to specify size automatically. You can either use automatic deduction from constructor or provide custom deduction guides. If you don't have C++17, you can create a helper function similar to make_pair/make_tuple which will employ template argument deduction and create the corresponding class.

#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <iostream>

template<std::size_t N>
struct WordStorage {
    std::array<char, N> char_array;
    WordStorage(const char (&char_array_)[N]) {
        std::copy(char_array_, char_array_ + N, char_array.begin());
    }
};

int main() {
    WordStorage s("hello");  // Deduced to WordStorage<6> as "hello" is const char[6].
    static_assert(s.char_array.size() == 6);
    std::cout << s.char_array.data() << "\n";
}
yeputons
  • 8,478
  • 34
  • 67
  • This seems to be the answer I was looking for for the most part - thank you so much. Just a question - how would I "provide custom deduction guides"? I quick Google search didn't really help much. Could you point me in the right direction? – Gary Allen Jun 12 '20 at 07:38
  • See [code examples here](https://en.cppreference.com/w/cpp/language/class_template_argument_deduction#User-defined_deduction_guides) – yeputons Jun 12 '20 at 19:32
0

probably closest thing you can get in my opinion :


template <char... c>
class foo
{
private:
    std::array<char, sizeof...(c)> m_arr;

public:
    foo()
    {
        std::cout << m_arr.size()<<'\n'; //ok : prints 3
    }
};

int main()
{
    foo<'c', 'h', 'a'> f;
}
Midnight Exigent
  • 615
  • 4
  • 15
  • Unfortunately this is highly unpractical for my use case :( unless maybe there's any easy way to make a macro that will have the same effect? I also ultimately need to process the incoming string somehow and make the array based on the number of words so this specific method isn't enough in that regard either.... hmm. – Gary Allen Jun 11 '20 at 21:55
  • @yeputons' answer with template argument deduction seems to work – Midnight Exigent Jun 11 '20 at 22:03