By default, the std::end overload for raw arrays looks something like this:
template<class T, std::size_t N>
T* end(T (&array)[N])
{ return array + N; }
However, this overload was undesirable for me when passing a string literal or a char array, as they both have an implicit \0 at the end which gets counted.
I thought as a workaround I could overload end in my own namespace:
Example:
namespace unique
{
const char* end(const char (&array)[5])
{
return array + 4;
}
const char* end(const char (&array)[11])
{
return array + 10;
}
}
int main()
{
using std::begin;
using std::end;
using unique::end;
const char str1[] = "XXXTEXTXXX";
const char str2[] = "TEXT";
auto iter = std::search(begin(str1), end(str1), begin(str2), end(str2));
//...
}
However, that would require a lot of overloads to write.
QUESTION
I realize using std::string or another container would solve my problem. However, I wanted to be able to call end unqualified with a string literal or raw array and have it omit the null terminator as above. Is there a better approach that would avoid writing the overloads?
The obvious (if you're sure it'll only be used under the right circumstances) would be a variant of the original:
namespace unique {
template<class T, std::size_t N>
T* end(T (&array)[N])
{ return array + N-1; }
}
Just be sure to only use it in the right situations, or you'll end up with end that points one before the end.
If you want to restrict it to character types, you can use a couple of overloads that only work for character types:
namespace unique {
template <std::size_t N>
char *end(char (&array)[N])
{
return array + N - 1;
}
template <std::size_t N>
wchar_t *end(wchar_t (&array)[N])
{
return array + N - 1;
}
}
With this, an array of char will use the version that assumes a NUL terminator, but an array of int will use std::end so it refers to the entire array:
int main()
{
using std::begin;
using std::end;
using unique::end;
char str1 [] = "12345";
wchar_t str2 [] = L"12345";
int i4 [] = { 1, 2, 3, 4, 5, 6 };
std::cout << std::distance(begin(str1), end(str1)) << "\n";
std::cout << std::distance(begin(str2), end(str2)) << "\n";
std::cout << std::distance(begin(i4), end(i4)) << "\n";
}
Do note, however, that since there's an existing template named begin, these overloads will only match exact types, so if you want them to work with const char and const wchar_t (for example) those will require separate overloads from the ones above that work with non-const types.
Also note that these will still apply to typedefs, so (for example) the fairly common:
typedef char small_int;
...can/will lead to problems -- the real type is still char, so end(my_small_int_array) will use the char overload instead of the base template.
Related
I need to write a class template definition with two template parameters (type , functor) and two template arguments (array/std::vector , int) that can execute the following code:
const char* message= "Message";
const transform<char, firstFunctor> first(message, lengthOfMessage);
transform_view<int, secondFunctor> second(intArray, intSize);
transform<double, thirdFunctor> third(doubleArray, doubleSize);
The type of the array/ vector has to match the type of the first template parameter.
I tried some variations like this:
template <typename A, typename B>
class transform
{
public:
transform<A, B>(A[], B) {...};
}
But I could not get the constructor's first parameter to match all of the three types.
Any advice is appreciated, thanks!
you wrote the definition of the constructor incorrectly.
transform<A, B>(A[], B) {...}; you will pass a vector, so why did you write A[] as a parameter type?
You need something like the following
#include <iostream>
template <typename T>
struct functor{
void operator()(const T array [], size_t sze) {
for (int i{}; i < sze; ++i) {
std::cout << array[i] << " ";
}
std::cout << "\n";
}
};
template<typename T, typename Function>
class transform {
const T* array;
size_t sze;
Function functor{};
public:
transform(const T array [], size_t sze):array{array}, sze{sze}{
functor(array, sze);
}
};
template< typename T, typename E>
using transform_view = transform<T, E>;
int main()
{
using firstFunctor = functor<char>;
using secondFunctor = functor<int>;
using thirdFunctor = functor<double>;
const char *message = "Message";
size_t lengthOfMessage = 7;
int intArray[] = {1, 3};
size_t intSize = 2;
double doubleArray[] = {1.4, 3.2};
size_t doubleSize = 2;
//The given three lines
const transform<char, firstFunctor> first(message, lengthOfMessage);
transform_view<int, secondFunctor> second(intArray, intSize);
transform<double, thirdFunctor> third(doubleArray, doubleSize);
}
The output
M e s s a g e
1 3
1.4 3.2
From what I understand, the problem is the first parameter being the array in the template.
Try the following constructor:
transform(A* arr, B el){
//now here is a tricky part, because there is a very big difference
//between a char*, and an int/double/unsigned/float/... *.
}
If you have an array of the type A in the class and want to change it to the one passed:
private:
A* my_array;
you can try sth like this:
if(dynamic_cast<char*>(arr)) //if arr is of type char* {
if (this->my_array != nullptr) delete my_array; //not needed if in a basic constructor...
size_t len = strlen(arr);
my_array = new char [len + 1];
strcpy(this->my_array, arr); //(destination, source)
my_array[len] = '\0';
}
else //if it is a numeric array
{
this->my_array = arr;
//redirecting the pointers in enough
}
Oh and if you are on Visual Studio, strcpy will work if you write
'#pragma warning (disable: 4996)' at the top of the file.
Otherwise it marks it as unsafe and suggests strncpy, strcpy_s, ...
I can't understand why the std::begin() function doesn't work when it is given an int * arr pointer, but it works with an int arr[] array.
This code doesn't work:
int *arr = new int[5]{ 1,2,3,4,5 };
if (find(begin(arr),end(arr),5)!=end(arr))
{
cout << "found";
}
This code does work:
int arr2[5] = { 1,2,3,4,5 };
if (find(begin(arr2),end(arr2),5)!=end(arr2))
{
cout << "found";
}
It doesn't work because int *arr isn't an array, it's a pointer. It happens to point to the first element of an array, but it's pointing to the element, not the array. Once it's converted to a pointer, the length information is lost.
Pedantic note: It's not completely lost, as it's actually hidden in an implementation-specific manner such that the allocator knows how much to free when you later delete[] it, but that's not exposed to anything but the allocator, and it's often not equal to the actual size requested, due to allocator alignment requirements, so it's not useful here.
std::(c)begin() and std:(c)end() are explicitly overloaded to work with fixed-sized array types. Information about the size of the arrays is not lost, as the sizes are part of the array types themselves. An implementation of these overloads may look something like this:
template<typename T, size_t N>
T* begin(T (&arr)[N])
{
return arr;
}
template<typename T, size_t N>
const T* cbegin(const T (&arr)[N])
{
return arr;
}
template<typename T, size_t N>
T* end(T (&arr)[N])
{
return arr + N;
}
template<typename T, size_t N>
const T* cend(const T (&arr)[N])
{
return arr + N;
}
Thus, std::begin(arr2) and std::end(arr2) are perfectly valid only when arr2 is an int[N] fixed-size array type. The compiler can deduce values for the T and N template parameters based on the type of fixed array being passed in, eg:
int arr2[5] = { 1,2,3,4,5 };
if (find(
begin(arr2), // deduces T=int, N=5, thus calls std::begin<int,5>(arr2)
end(arr2), // deduces T=int, N=5, thus calls std::end<int,5>(arr2)
5)
!= end(arr2) // deduces T=int, N=5, thus calls std::end<int,5>(arr2)
)
{
cout << "found";
}
Conversely, there is simply no way to provide similar 1-parameter overloads for an int* pointer, as there is no information about the size of the array being pointed at (or even whether an array is even being pointed at) for std::end() to return a valid iterator to the end of the array:
template<typename T>
T* begin(T *arr)
{
return arr; // OK
}
template<typename T>
const T* cbegin(const T *arr)
{
return arr; // OK
}
template<typename T>
T* end(T *arr)
{
return arr + N; // NOT OK, WHAT IS N SUPPOSED TO BE?!?
}
template<typename T>
const T* cend(const T *arr)
{
return arr + N; // NOT OK, WHAT IS N SUPPOSED TO BE?!?
}
However, you can provide your own 2-parameter overloads 1, so you can explicitly pass in the dynamic array size for N, eg:
namespace std
{
template<typename T>
T* begin(T *arr, size_t N)
{
return arr;
}
template<typename T>
const T* cbegin(const T *arr, size_t N)
{
return arr;
}
template<typename T>
T* end(T *arr, size_t N)
{
return arr + N;
}
template<typename T>
const T* cend(const T *arr, size_t N)
{
return arr + N;
}
}
int *arr = new int[5]{ 1,2,3,4,5 };
if (find(
begin(arr,5), // deduces T=int, explicit N=5, thus calls std::begin<int>(arr2,5)
end(arr2,5), // deduces T=int, explicit N=5, thus calls std::end<int>(arr2,5)
5)
!= end(arr2,5) // deduces T=int, explicit N=5, thus calls std::end<int>(arr2,5)
)
{
cout << "found";
}
Live Demo
1: you ARE NOT allowed to add new functions to the std namespace, but you ARE allowed to add custom overloads for existing standard functions. std::swap() is an common example of that, though utilizing ADL is generally a better choice than extending the std namespace.
The std::begin docs :
template< class C >
auto begin( C& c ) -> decltype(c.begin());
Returns an iterator to the beginning of the given container c or array. These templates rely on C::begin() having a reasonable implementation.
int *arr is not an array, but a pointer, while int arr2[5] is an array.
I've got a utility called choose_literal which chooses a literal string encoded as char*, wchar_*, char8_t*, char16_t*, char32_t* depending on the desired type (the choice).
It looks like this:
template <typename T>
constexpr auto choose_literal(const char * psz, const wchar_t * wsz, const CHAR8_T * u8z, const char16_t * u16z, const char32_t * u32z) {
if constexpr (std::is_same_v<T, char>)
return psz;
if constexpr (std::is_same_v<T, wchar_t>)
return wsz;
#ifdef char8_t
if constexpr (std::is_same_v<T, char8_t>)
return u8z;
#endif
if constexpr (std::is_same_v<T, char16_t>)
return u16z;
if constexpr (std::is_same_v<T, char32_t>)
return u32z;
}
I supply a little preprocessor macro to make this work w/o having to type each of those string encodings manually:
// generates the appropriate character literal using preprocessor voodoo
// usage: LITERAL(char-type, "literal text")
#define LITERAL(T,x) details::choose_literal<T>(x, L##x, u8##x, u##x, U##x)
This of course only works for literal strings which can be encoded in the target format by the compiler - but something like an empty string can be, as can ASCII characters (i.e. a-z, 0-9, etc., which have representations in all of those encodings).
e.g. here's a trivial bit of code that will return the correct empty-string given a valid character type 'T':
template <typename T>
constexpr const T * GetBlank() {
return LITERAL(T, "");
}
This is great as far as it goes, and it works well enough in my code.
What I'd like to do is to refactor this such that I get back the character-array including its size, as if I'd written something like:
const char blank[] = "";
or
const wchar_t blank[] = L"";
Which allows the compiler to know the length of the string-literal, not just its address.
My choose_literal<T>(str) returns only the const T * rather than the const T (&)[size] which would be ideal.
In general I'd love to be able to pass such entities around intact - rather than have them devolve into just a pointer.
But in this specific case, is there a technique you might point me towards that allows me to declare a struct with a data-member for the desired encoding which then also knows its array-length?
A little bit of constexpr recursion magic allows you to return a string_view of the appropriate type.
#include <string_view>
#include <type_traits>
#include <iostream>
template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
using const_char_type = Choice;
using char_type = std::remove_const_t<const_char_type>;
if constexpr (std::is_same_v<T, char_type>)
{
constexpr auto extent = N;
return std::basic_string_view<char_type>(choice, extent - 1);
}
else
{
return choose_literal<T>(rest...);
}
}
int main()
{
auto clit = choose_literal<char>("hello", L"hello");
std::cout << clit;
auto wclit = choose_literal<wchar_t>("hello", L"hello");
std::wcout << wclit;
}
https://godbolt.org/z/4roZ_O
If it were me, I'd probably want to wrap this and other functions into a constexpr class which offers common services like printing the literal in the correct form depending on the stream type, and creating the correct kind of string from the literal.
For example:
#include <string_view>
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
using const_char_type = Choice;
using char_type = std::remove_const_t<const_char_type>;
if constexpr (std::is_same_v<T, char_type>)
{
constexpr auto extent = N;
return std::basic_string_view<char_type>(choice, extent - 1);
}
else
{
return choose_literal<T>(rest...);
}
}
template<class...Choices>
struct literal_chooser
{
constexpr literal_chooser(Choices&...choices)
: choices_(choices...)
{}
template<class T>
constexpr auto choose()
{
auto invoker = [](auto&...choices)
{
return choose_literal<T>(choices...);
};
return std::apply(invoker, choices_);
}
std::tuple<Choices&...> choices_;
};
template<class Char, class...Choices>
std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os, literal_chooser<Choices...> chooser)
{
return os << chooser.template choose<Char>();
}
template<class Char, class...Choices>
std::basic_string<Char> to_string(literal_chooser<Choices...> chooser)
{
auto sview = chooser.template choose<Char>();
return std::basic_string<Char>(sview.data(), sview.size());
}
int main()
{
auto lit = literal_chooser("hello", L"hello");
std::cout << lit << std::endl;
std::wcout << lit << std::endl;
auto s1 = to_string<char>(lit);
auto s2 = to_string<wchar_t>(lit);
std::cout << s1 << std::endl;
std::wcout << s2 << std::endl;
}
The use of the reference argument type Choices& is important. C++ string literals are references to arrays of const Char. Passing by value would result in the literal being decayed into a pointer, which would lose information about the extent of the array.
we can add other services, written in terms of the literal_chooser:
template<class Char, class...Choices>
constexpr std::size_t size(literal_chooser<Choices...> chooser)
{
auto sview = chooser.template choose<Char>();
return sview.size();
}
We're going to change the function so that it takes a const T (&)[size] for each input, and the return type is going to be decltype(auto). Using decltype(auto) prevents the return from decaying into a value, preserving things like references to arrays.
Updated function:
template <typename T, size_t N1, size_t N2, size_t N3, size_t N4>
constexpr decltype(auto) choose_literal(const char (&psz)[N1], const wchar_t (&wsz)[N2], const char16_t (&u16z)[N3], const char32_t (&u32z)[N4]) {
if constexpr (std::is_same<T, char>())
return psz;
if constexpr (std::is_same<T, wchar_t>())
return wsz;
if constexpr (std::is_same<T, char16_t>())
return u16z;
if constexpr (std::is_same<T, char32_t>())
return u32z;
}
In main, we can assign the result to something of type auto&&:
#define LITERAL(T,x) choose_literal<T>(x, L##x, u##x, U##x)
int main() {
constexpr auto&& literal = LITERAL(char, "hello");
return sizeof(literal); // Returns 6
}
Potential simplification
We can simplify the choose_literal function by making it recursive, that way it can be expanded for any number of types. This works without any changes to the LITERAL macro.
template<class T, class Char, size_t N, class... Rest>
constexpr decltype(auto) choose_literal(const Char(&result)[N], Rest const&... rest) {
if constexpr(std::is_same_v<T, Char>)
return result;
else
return choose_literal<T>(rest...);
}
I'm working on some API for algorithm involving text.
I would like to make it NOT dependent on the character type (char,wchar_t...), so I have made template classes with a template parameter CharT.
These classes use std::basic_string<CharT>.
I have to initialize a lot of basic_string with default values.
If CharT is char I can affect the literal "default_text", or if CharT is wchar_t I can affect L"default_text", but this is not generic (it is CharT dependant).
Do you think of any way to initialize the basic_string with a generic method ?
If that may help, my code is in C++11.
Since your code is generic, I guess that the literal you have only contains ASCII characters. Otherwise, you'd have to transcode it on the fly which is going to be a lot of hassle. In order to promote a pure-ASCII string literal of type char[] to another character type, you can simply promote each character individually.
If you're going to initialize a std::basic_string anyway, you can as well do it right away. The following function takes a char[] string literal and a target type and promotes it to a string of that type.
template <typename CharT>
auto
as_string(const char *const text)
{
const auto length = std::strlen(text);
auto string = std::basic_string<CharT> {};
string.resize(length);
for (auto i = std::size_t {}; i < length; ++i)
string[i] = CharT {text[i]};
return string;
}
It can be used like this.
std::cout << as_string<char>("The bats are in the belfry") << '\n';
std::wcout << as_string<wchar_t>("The dew is on the moor") << L'\n';
But you've asked for a character array, not a std::basic_string. In C++14, constexpr can help a lot with this. Be warned that you'd need the most recent compilers for this to be supported well.
The first thing we'll have to do is rolling our own version of std::array that provides constexpr operations. You can get as fancy as you want to but I'll keep it simple here.
template <typename T, std::size_t N>
struct array { T data[N]; };
Next, we also need a constexpr version of std::strlen.
template <typename CharT>
constexpr auto
cstrlen(const CharT *const text) noexcept
{
auto length = std::size_t {};
for (auto s = text; *s != CharT {0}; ++s)
++length;
return length;
}
Now we can write a constexpr function that promotes us a string literal.
template <typename CharT, std::size_t Length>
constexpr auto
as_array(const char *const text)
{
auto characters = array<CharT, Length + 1> {};
if (cstrlen(text) != Length)
throw std::invalid_argument {"Don't lie about the length!"};
for (auto i = std::size_t {}; i < Length; ++i)
characters.data[i] = text[i];
characters.data[Length] = CharT {0};
return characters;
}
It might be convenient to wrap it into a macro. I'm sorry for that.
#define AS_ARRAY(Type, Text) as_array<Type, cstrlen(Text)>(Text).data
It can be used like this.
std::cout << AS_ARRAY(char, "The bats are in the belfry") << '\n';
std::wcout << AS_ARRAY(wchar_t, "The dew is on the moor") << L'\n';
I am a bit in stuck and need a help from C++ template guru. There is a template struct:
template<typename T, typename ID>
struct TypeMapping
{
T Type;
char* Name;
ID Id;
};
and a few template functions like this:
template<typename T, typename ID>
bool TryGetTypeByNameImp(const TypeMapping<T, ID> map[], size_t mapSize,
const char* name, T& type)
{
for (size_t i = 0; i < mapSize; i++)
{
if (strcmp(map[i].Name, name) == 0)
{
type = map[i].Type;
return true;
}
}
return false;
}
Map (the first parameter) is defined as (there are a few similar maps)
namespace audio
{
const TypeMapping<Type, AMF_CODEC_ID> Map[] =
{
{AAC, "aac", AMF_CODEC_AAC},
{MP3, "mp3", AMF_CODEC_MP3},
{PCM, "pcm", AMF_CODEC_PCM_MULAW}
};
const size_t MapSize = sizeof(Map)/sizeof(Map[0]);
}
Map is passed to a function as an argument and I am looking for how to pass it as template parameter so I can use functions like in this sample:
audio::Type type;
bool r = TryGetTypeByNameImp<audio::Map>("aac", type);
The only solution I found it is to define a struct which holds static Map and MapSize and use the struct as template parameter but I do not like this solution and I am looking for another one. Does anybody know how to do this?
bool r = TryGetTypeByNameImp<audio::Map>("aac", type);
This is trying to use audio::Map as a type – but it isn’t, it’s a variable. Just pass it to the function as a normal argument:
bool r = TryGetTypeByNameImp(audio::Map, "aac", type);
That said, I have three remarks about your code:
Be aware that declaring a function argument as an array (x[]) does in reality declare it as a pointer. Your code uses this correctly, but using the array syntax is misleading. Use a pointer instead.
This code is slightly too C-heavy for my taste. While I agree that using raw C-strings is appropriate here, your usage of char* is illegal in C++11, and deprecated in C++03 (since you are pointing to string literals). Use char const*. Furthermore, I’d suggest using a std::string argument in the function, and using the comparison operator == instead of strcmp.
You are using an out-parameter, type. I abhor this technique. If you want to return a value, use the return type. Since you also return a success value, use a pair as the return type, unless there’s a very compelling reason not to:
template<typename T, typename ID>
std::pair<bool, T> TryGetTypeByNameImp(
const TypeMapping<T, ID> map[], size_t mapSize,
const char* name)
{
for (size_t i = 0; i < mapSize; i++)
if (strcmp(map[i].Name, name) == 0)
return std::make_pair(true, map[i].Type);
return std::make_pair(false, T());
}
Ah, and I’d also consider using a std::vector or std::array here instead of a C array. Then you don’t need to manually shlep the array size around through all the functions which use the array.
You can certainly use the array itself (well, a pointer to it) as a template parameter:
#include <iostream>
template<typename T> struct S { T t; };
S<int> s[] = { { 21 }, { 22 } };
template<typename T, size_t n, S<T> (*m)[n]> void f() { std::cout << (*m)[n - 1].t; }
int main() {
f<int, 2, &s>();
}
The problem here is that you can't use template argument deduction on the length of the array nor on its type, so both must be supplied as template parameters in addition to the array itself. I really think that passing in a struct or, say a vector would be the better solution, as you've no doubt already explored:
#include <vector>
#include <iostream>
template<typename T> struct S { T t; };
std::vector<S<int>> s{ { 21 }, { 22 } };
template<typename T, std::vector<S<T>> *v> void f() { std::cout << v->back().t; }
int main() {
f<int, &s>();
}