Given some function void func(std::span<std::string_view>)
, how does one feed this function a raw array of C-strings const char**
in the most efficient manner?
As far as I understood this should be possible as without any copying as std::string_view
can be constructed from a C string and std::span
can be constructed from a pointer + size. However, I don't seem to be able to figure out the proper syntax.
Here's some minimal code that works when creating an std::vector<std::string_view>
by iterating over each string in the string array:
#include <iostream>
#include <string>
#include <span>
#include <vector>
static void func(std::span<std::string_view> strings)
{
// Just print the strings...
std::cout << "strings:\n";
for (const auto& str : strings)
std::cout << str << "\n";
std::cout << std::endl;
}
int main()
{
// Raw C string array
const char* raw_strings[3] = {
"This is a string",
"This is also a string",
"And then another string"
};
#if 1
// This works
std::vector<std::string_view> s;
for (std::size_t i = 0; i < 3; i++)
s.push_back( std::string_view{ raw_strings[i] } );
#else
// This does not work
std::span<std::string_view> s{ raw_strings, (std::size_t)3 };
#endif
func(s);
}
Here's a coliru link with the grunt work done: https://coliru.stacked-crooked.com/a/cb8fb8ebbc962d45
All of the big three C++ compilers MSVC, GCC, and Clang, support std::span. There are more ways to create a std::span. You can create a std::span from a pointer and a size. As you may expect, the from a std::vector created mySpan1 (1) and the from a pointer and a size created mySpan (2) are equal (3).
From this brief description, two things should be clear: Span<T> is defined in such a way that operations can be as efficient as on arrays: indexing into a span doesn’t require computation to determine the beginning from a pointer and its starting offset, as the ref field itself already encapsulates both.
In C++, a string is usually just an array of (or a reference/points to) characters that ends with the NULL character ‘ \0 ‘. A string is a 1-dimensional array of characters and an array of strings is a 2-dimensional array of characters. 1. Using Pointers
With C++20, the answer is quite easy: Use a std::span. A std::span stands for an object that can refer to a contiguous sequence of objects. A std::span, sometimes also called a view, is never an owner. This contiguous memory can be a plain array, a pointer with a size, a std::array, a std::vector, or a std::string.
If you think about it, to do what you are asking the compiler needs to find somewhere to store 3 std::string_view
s and there is no such place since std::span
doesn't actually allocate any storage for the elements it references.
So, AFAICS, there's no direct way to do this.
Edit: this, I think, is the cheapest way to do it, since it involves no allocations from the free store. Take care, though, that the array doesn't go out of scope while the span is being used:
std::array <std::string_view, sizeof (raw_strings) / sizeof (raw_strings [0])> sa;
std::copy (std::begin (raw_strings), std::end (raw_strings), std::begin (sa));
std::span <std::string_view> s { sa };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With