I'm trying to write a function that would return a substring from the given start and end indices. This is the code that I've written but when i run it it gives me error. I'm using gtest to run it not main().
template <typename S>
S substring(S string_, int Istart, int Iend)
{
S substr = string_[Istart];
for(int i=(Istart+1); i<Iend; i++)
{
substr += string_[i];
}
return substr;
}
And this is the error i get:
In file included from test.cpp:2:0:
lab2.cpp: In instantiation of ‘S substring(S, int, int) [with S = const char*]’:
test.cpp:56:1: required from here
lab2.cpp:91:23: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
S substr = string_[Istart];
~~~~~~~^
My test code is this:
TEST(subStr, T1){
string str="he";
EXPECT_EQ(str,substring("hello", 0, 2));
}
TEST(subStr, T2){
string str="urge";
EXPECT_EQ(str,substring("hamburger", 4, 8));
}
With the given calling context, this:
EXPECT_EQ(str,substring("hello", 0, 2));
utilizes an expansion of
substring<const char*>
and therefore the resulting code becomes:
const char* substring(const char* string_, int Istart, int Iend)
{
const char* substr = string_[Istart];
for(int i=(Istart+1); i<Iend; i++)
{
substr += string_[i];
}
return substr;
}
Clearly that's not going to work. const char* substr = string_[Istart]; is initializing const char * from char. As I see it you have two choices, but only one of them remotely realistic. Since EXPECT_EQ tests equivalence, going the pointer route will not work no matter what. You need to have a comparable std::string guaranteed on at least one side of that test, and the only way to guarantee that whilst still affording your expressive arguments is like this:
template<class S>
std::string substring(S s, int Istart, int Iend)
{
return std::string(s).substr(Istart, Iend - Istart);
}
This will take anything compatible to std::string as a source argument. The result is ALWAYS a std::string, which can then be used in comparison against a variety of things, including char[N], const char *, and of course, std::string.
It still has a huge caveat, that being Istart and Iend must be ordered. Eliminating that frailty is the very reason the standard library substr member of std::string doesn't take a begin,end; it takes a begin,length. Nonetheless, this is easily the simplest way to do what you want.(the single-line nature calling out the very usefulness of such a thing notwithstanding).
Example
I don't have GoogleTest, but a simple assert macro will demonstrate testing against different comparisons to std::string will work:
#include <iostream>
#include <string>
#include <cassert>
template<class S>
std::string substring(S s, int Istart, int Iend)
{
return std::string(s).substr(Istart, Iend - Istart);
}
int main()
{
const char *kvalptr = "welcome";
std::string kvalstr = kvalptr;
auto res = substring("abdwelcomedef", 3, 10);
// test that both lhs prospects test against our result
assert(kvalptr == res);
assert(kvalstr == res);
// output res and one of the prospects.
std::cout << res << '\n' << kvalptr << '\n';
}
Output
welcome
welcome
Assuming that the intended type is string. A += operator exists for the string type.
A simple fix is to replace
S substr = string_[Istart];
with
S substr;
substr += string_[Istart];
which will not work for const char* types as pointed out
The following works for both string and const char*.
#include <exception>
#include <iostream>
#include <functional>
#include <string>
#include <type_traits>
template <typename S>
S substring(S string_, int Istart, int Iend)
{
if constexpr (std::is_same_v<S, std::string>) {
if(Istart < Iend){
return string_.substr(Istart, Iend-Istart);
}
}
else if constexpr (std::is_same_v<S, const char*>)
{
if(Istart < Iend){
std::string temp = std::string(string_);
return (temp.substr(Istart, Iend-Istart)).c_str();
}
}
else throw;
}
Related
Context:
In my company we generate a lot of types based on IDL files. Some of the types require special logic so they are handcoded but follow the same pattern as the generated ones. We have a function which all types must implement which is a name function. This will return the type name as a char* string and the function is constexpr.
Problem:
The problem is regarding collections which could contain other collections nested potentially N number of times. I therefore am trying to concatenate two or more char* strings at compile time.
Pseudocode of what I want to achieve:
template <typename T>
constexpr char* name()
{
constexpr char* collectionName = "std::vector";
constexpr char* containedTypeName = name<T>();
return concat(collectionName, "<", containedTypeName, ">");
}
Note:
There are examples out there which does something like this but is done with char[] or the use of static variables.
The question:
How can I make a constexpr function which return a char* which consists of two or more concatenated char* strings at compile time? I am bound to C++17.
From constexpr you cannot return char* which is constructed there... You must return some compile time known(also its size) constant thingy.
A possible solution could be something like:
#include <cstring>
// Buffer to hold the result
struct NameBuffer
{
// Hardcoded 128 bytes!!!!! Carefully choose the size!
char data[128];
};
// Copy src to dest, and return the number of copied characters
// You have to implement it since std::strcpy is not constexpr, no big deal.
constexpr int constexpr_strcpy(char* dest, const char* src);
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr NameBuffer name()
{
// We will return this
NameBuffer buf{};
constexpr const char* collectionName = "std::vector";
constexpr const char* containedTypeName = "dummy";
// Copy them one by one after each other
int n = constexpr_strcpy(buf.data, collectionName);
n += constexpr_strcpy(buf.data + n, "<");
n += constexpr_strcpy(buf.data + n, containedTypeName);
n += constexpr_strcpy(buf.data + n, ">");
// Null terminate the buffer, or you can store the size there or whatever you want
buf.data[n] = '\0';
return buf;
}
Demo
And since the returned char* is only depends on the template parameter in your case, you can create templated variables, and create a char* to them, and it can act like any other char*...
EDIT:
I have just realized that your pseudo code will never work!! Inside name<T>() you are trying to call name<T>().
You must redesign this!!! But! With some hack you can determine the size at compile time somehow for example like this:
#include <cstring>
#include <iostream>
template<std::size_t S>
struct NameBuffer
{
char data[S];
};
// Copy src to dest, and return the number of copied characters
constexpr int constexpr_strcpy(char* dest, const char* src)
{
int n = 0;
while((*(dest++) = *(src++))){ n++; }
return n;
}
// Returns the len of str without the null term
constexpr int constexpr_strlen(const char* str)
{
int n = 0;
while(*str) { str++; n++; }
return n;
}
// This template parameter does nothing now...
// I left it there so you can see how to create the template variable stuff...
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr auto createName()
{
constexpr const char* collectionName = "std::vector";
constexpr const char* containedTypeName = "dummy";
constexpr std::size_t buff_size = constexpr_strlen(collectionName) +
constexpr_strlen(containedTypeName) +
2; // +1 for <, +1 for >
/// +1 for the nullterm
NameBuffer<buff_size + 1> buf{};
/// I'm lazy to rewrite, but now we already calculated the lengths...
int n = constexpr_strcpy(buf.data, collectionName);
n += constexpr_strcpy(buf.data + n, "<");
n += constexpr_strcpy(buf.data + n, containedTypeName);
n += constexpr_strcpy(buf.data + n, ">");
buf.data[n] = '\0';
return buf;
}
// Create the buffer for T
template<typename T>
static constexpr auto name_buff_ = createName<T>();
// point to the buffer of type T. It can be a function too as you wish
template<typename T>
static constexpr const char* name = name_buff_<T>.data;
int main()
{
// int is redundant now, but this is how you could use this
std::cout << name<int> << '\n';
return 0;
}
Demo
I have a template function, within which I need to convert a const char * to my template value.
I know for a fact that this const char * was originally read from a ascii text file. My current code looks like this:
template <typename T>
bool Get(T &value, std::string const &query, T const &default)
{
const char* result = DataHandler.GetValue(query);
if (result != NULL)
{
value = static_cast<T>(result); //Here is the issue
return true;
}
value = default
return false;
}
Using this with an int as example i get the error
error C2440: 'static_cast' : cannot convert from 'const char *' to 'int'
Is there a way I can convert the char* to my type T seamlessly, I could not find an answer for this on SO.
In the worst case I can make a case for the 10 types I expect and give an error if not one of these, but I would rather not do it that way if possible.
Is there a way I can convert the char* to my type T seamlessly,
No.
There is no way to make conversion from string to a type automatically work with all types. Such conversion has to be implemented for each class. Typically, it is done by implementing the stream extraction operator >> for std::istream. The built in types such as int and some standard types such as std::string already have such operator. Then you can do for example:
std::istringstream istream(result);
int i;
istream >> i;
Without knowing exactly what you are asking for, using the answer given by #eerorika, here is a generic way to do the conversion:
#include <sstream>
#include <string>
#include <iostream>
template <typename T>
struct DefaultConverter
{
static T Convert(const char *result)
{
std::istringstream istream(result);
T i;
istream >> i;
return i;
}
};
template <typename T, typename Converter = DefaultConverter<T>>
bool Get(T &value, std::string const &query, T const &defaultVal)
{
const char* result = "100";
value = Converter::Convert(result);
return true;
}
int main()
{
int test = 10;
Get(test, "abc123", test);
std::cout << test;
}
The code referring to the DataHandler function is not important to illustrate what the above does.
Basically, the Get function takes an additional argument, namely a conversion template that has an available Convert function that can be called by Get. Note that the default converter simply uses the code as illustrated in the previous answer given by #eerorika.
This gives you the flexibility of providing your own converter that has a Convert function that can do anything customized.
For example:
struct SomeMPEGObject
{
SomeMPEGObject() {}
SomeMPEGObject(const char *) {}
};
struct MPEGConverter
{
static SomeMPEGObject Convert(const char *result)
{
return SomeMPEGObject(result);
}
};
//...
SomeMPEGObject mpeg;
Get<SomeMPEGObject, MPEGConverter>(mpeg, "12345", mpeg);
This will work without changing any code in the Get function.
std::stoi is throwing some errors in specific cases. I don't want to use try/catch block, so I googled a little bit about char convertion and saw that std::from_chars was doing exactly what I wanted, without those try/catch block.
Obviously, using std::from_chars directly works pretty well, but I don't like the syntax. So I started writing my own ToInt(), ToFloat(), etc. And was like : "ok, that sounds stupid, let's use template".
Here I am :
#include <string>
#include <charconv>
#include <iostream>
template <typename T>
T ToNumber(const char* str, T varType)
{
if (!str)
return 0;
T var = 0;
std::from_chars(str, str + strlen(str), var);
return var;
}
int main()
{
std::string t = "123.5";
auto a = ToNumber(t.c_str(), (int)0); // a will be an int (123)
float b = ToNumber(t.c_str(), (int)0); // b will store the value as int (123)
auto c = ToNumber(t.c_str(), (float)0); // c will be a float (123.5)
std::cout << a;
return 0;
}
It actually works, that is not the problem. But I was wondering... Is it a good practice to send a "false argument" (T varType) in order to get the cast done and enable auto ?
If not, what is the clever way to write such a function ?
Thanks in advance
You are not checking the return value of std::from_chars for failure, eg:
auto [p, ec] = std::from_chars(str, str + strlen(str), var);
if (ec != std::errc()) {
...
}
That said, another way to deal with the template argument T without using a type-casted input parameter is to simply specify the desired type explicitly at the call site instead, eg:
template <typename T>
T ToNumber(const char* str)
{
if (!str)
return T{};
T var{};
auto [p, ec] = std::from_chars(str, str + strlen(str), var);
if (ec != std::errc())
return T{};
return var;
}
auto a = ToNumber<int>(t.c_str());
float b = ToNumber<int>(t.c_str());
auto c = ToNumber<float>(t.c_str());
Otherwise, you can use template argument deduction via a reference output parameter (just like std::from_chars() does), eg:
template <typename T>
bool ToNumber(const char* str, T &var)
{
if (!str)
return false;
auto [p, ec] = std::from_chars(str, str + strlen(str), var);
return (ec == std::errc());
}
int a;
ToNumber(t.c_str(), a);
int tmp;
ToNumber(t.c_str(), tmp);
float b = tmp;
float c;
ToNumber(t.c_str(), c);
bool foo(const char* arg, const char* str[]){
for (int i=0; i < (signed)sizeof(str); i++){
if(strcmp(arg, str[i])) == 0){
return true;
}
}
return false;
}
int main(){
foo("c", {"a", "b", "c"});
return 0;
}
I want to pass an array of const char* to the function directly like it is shown here. But I keep getting the error: cannot convert '<brace-enclosed initializer list>' to 'const char**'
While there is a way to make that work using templates and passing C-arrays by reference, I'm not sure if that is what you really want: example
In C++11 I'd much prefer to use a std::intializer_list to make the call foo("c", {"a", "b", "c"}) work as intended:
#include <initializer_list>
bool foo(const char* arg, std::initializer_list<const char*> strings) {
for (const char* str : strings) {
/* ... */
}
}
While we're at it, you should consider using C++'s string facilities over those inherited from C:
#include <initializer_list>
#include <string>
bool foo(const std::string& arg, std::initializer_list<std::string> strings) {
for (const auto& str : strings) {
if (arg == str) {
return true;
}
}
return false;
}
Note that a std::initializer_list does not own the values it provides access to, so if you want to store it, use a proper container like std::vector instead.
Also, if checking for the presence of arg in str is all you want to do,
why not use std::find?
#include <algorithm>
#include <initializer_list>
#include <string>
bool foo(const std::string& arg, std::initializer_list<std::string> strings) {
return std::find(strings.begin(), strings.end(), arg) != strings.end();
}
Use a character pointer pointer (char**) to achieve this. You cannot declare parameters as arrays, but you can pass arrays into functions, as they will be converted into double pointers.
You can also pass the array by reference via a template function, so you will be able to detect its size at compile time (note that in your case the array decays to a pointer, so sizeof doesn't do what you think). Example:
#include <iostream>
#include <cstring>
template<int N>
bool foo(const char* arg, const char* (&str)[N])
{
for (std::size_t i = 0; i < N; i++) {
if (std::strcmp(arg, str[i]) == 0) {
return true;
}
}
return false;
}
int main() {
const char* arr[] = {"abc", "de"};
std::cout << std::boolalpha << foo("de", arr);
}
Live on Coliru
And btw, try to use algorithms from the C++ standard library and std::string instead of strcmp() and raw C-strings:
#include <algorithm>
#include <iostream>
#include <vector>
bool foo(const std::string arg, const std::vector<std::string>& str)
{
return std::find(str.begin(), str.end(), arg) != str.end();
}
int main()
{
std::cout << std::boolalpha << foo("de", {"abc", "de"});
}
Live on Coliru
Although it is recommended to drop C-style programming when using C++, and use stl containers instead, I would like to mention that the following "one-liner" works:
bool foo(const char* arg, const char* const* str, size_t siz) {
for (size_t i = 0; i < siz; i++)
if (!strcmp(arg, str[i])) return true;
return false;
}
int main() {
cout << foo("c", array<char*, 3> { "a", "b", "c" }.data(), 3);
system("pause");
}
The surprising surprise is that if we define the second argument of foo as const char**, the compiler (VS2015) yells and says:
bool foo(...): cannot convert argument 2 from 'char **' to 'const char **'
:O
cannot convert from non-const to const??? Does that make sense anyone?
Edit: #NeilKirk explained it in his comment. Normal. Accordingly the signature should be as:
bool foo(const char* arg, const char* const* str, size_t siz)
and everything works as expected.
The Ghostscript interpreter API has a function
GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv)
The final argument argv is a pointer to an array of C strings, which are interpreted as command-line arguments. I obviously cannot change the signature of the function gsapi_init_with_args to take a const char ** argument instead.
If I were willing to ignore (or silence) the deprecated conversion from string constant to 'char*' warning, then I would write simply
char *gs_argv[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and pass gs_argv as the final argument. But I would prefer to fix my code so that I am not relying on an external function to behave in the way I expect it to (and effectively treat gs_argv as const char**).
Is there any simple way to declare gs_argv as an array of pointers to (non-const) C strings, and initialize its elements with string literals? (That is, using a similar approach to how I can initialize a single C string: using char c_str[] = "abc".) The best I can think of is to use
const char *gs_argv0[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and then copy the contents, element by element, into gs_argv.
Please note that I understand why the compiler gives this warning (and have read the answers to, among others, this question). I am asking for a solution, rather than an explanation.
You can use:
char arg1[] = "";
char arg2[] = "-dNOPAUSE";
char arg3[] = "-dBATCH";
char* gs_argv0[] = {arg1, arg2, arg3, NULL};
int argc = sizeof(gs_argv0)/sizeof(gs_argv0[0]) - 1;
gsapi_init_with_args(instance, argc, gs_argv0)
Create copies of the string literals using strdup. This is more verbose, but fixes the warning.
char* gs_argv0[NARGS];
gs_argv0[0] = strdup("");
gs_argv0[1] = strdup("-dNOPAUSE");
// ...
Note that you will also need to free the memory allocated by strdup if you want to prevent leaks.
You might also want to add a comment to your code saying why you are doing this, to make it clear for future readers.
If you can guarantee that the function will not modify the non-const parameter, then it is acceptable to use const_cast in this situation.
A C++14 solution.
#define W(x) \
(([](auto& s)->char* \
{ \
static char r[sizeof(s)]; \
strcpy (r, s); \
return r; \
})(x))
char* argv[] =
{ W("--foo=bar",
W("baz"),
nullptr
};
Since this code requires C++11, there's a lower cost C++11 solution in another answer below. I'm leaving this one for posterity.
There are pretty much two choices: ignore it and const_cast, or do the right thing. Since this is modern C++, you're supposed to have nice, RAII classes. Thus, the simplest, safest thing to do is to safely wrap such an array.
// https://github.com/KubaO/stackoverflown/tree/master/questions/args-cstrings-32484688
#include <initializer_list>
#include <type_traits>
#include <cstdlib>
#include <cassert>
#include <vector>
class Args {
struct str_vector : std::vector<char*> {
~str_vector() { for (auto str : *this) free(str); }
} m_data;
void append_copy(const char * s) {
assert(s);
auto copy = strdup(s);
if (copy) m_data.push_back(copy); else throw std::bad_alloc();
}
public:
Args(std::initializer_list<const char*> l) {
for (auto str : l) append_copy(str);
m_data.push_back(nullptr);
}
template <std::size_t N>
Args(const char * const (&l)[N]) {
for (auto str : l) append_copy(str);
m_data.push_back(nullptr);
}
/// Initializes the arguments with a null-terminated array of strings.
template<class C, typename = typename std::enable_if<std::is_same<C, char const**>::value>::type>
Args(C l) {
while (*l) append_copy(*l++);
m_data.push_back(nullptr);
}
/// Initializes the arguments with an array of strings with given number of elements.
Args(const char ** l, size_t count) {
while (count--) append_copy(*l++);
m_data.push_back(nullptr);
}
Args(Args && o) = default;
Args(const Args &) = delete;
size_t size() const { return m_data.size() - 1; }
char ** data() { return m_data.data(); }
bool operator==(const Args & o) const {
if (size() != o.size()) return false;
for (size_t i = 0; i < size(); ++i)
if (strcmp(m_data[i], o.m_data[i]) != 0) return false;
return true;
}
};
Let's see how it works:
#include <iostream>
extern "C" int gsapi_init_with_args(void*, int argc, char** argv) {
for (int i = 0; i < argc; ++i)
std::cout << "arg " << i << "=" << argv[i] << std::endl;
return 0;
}
int main()
{
Args args1 { "foo", "bar", "baz" };
const char * args2i[] { "foo", "bar", "baz", nullptr };
Args args2 { (const char **)args2i };
const char * args3i[] { "foo", "bar", "baz" };
Args args3 { args3i };
const char * const args4i[] { "foo", "bar", "baz" };
Args args4 { args4i };
const char * args5i[] { "foo", "bar", "baz" };
Args args5 { args5i, sizeof(args5i)/sizeof(args5i[0]) };
assert(args1 == args2);
assert(args2 == args3);
assert(args3 == args4);
assert(args4 == args5);
gsapi_init_with_args(nullptr, args1.size(), args1.data());
}
Output:
arg 0=foo
arg 1=bar
arg 2=baz
Try to const_cast it:
gsapi_init_with_args(instance, argc, const_cast<char**>(argv));
Maybe it will help with fixing warning.
Inspired by n.m.'s C++14 version, here's a C++11 version. The trick is to use an evaluated empty lambda expression to generate a fresh type, so that each instantiation of W__ is unique.
template <typename T, int N> static char * W__(const char (&src)[N], T) {
static char storage[N];
strcpy(storage, src);
return storage;
}
#define W(x) W__(x, []{})
char * argv[] = {
W("foo"),
W("bar")
};
The static in front of W__'s return type means that W__ has internal linkage and won't bloat the object file with extra symbols. It has nothing to do with the static in front of storage, as the latter indicates the static storage duration for the local variable. The code below would be perfectly valid, but of course doing the wrong thing and having undefined behavior:
template <typename T, int N> static char * BAD(const char (&src)[N], T) {
char storage[N];
strcpy(storage, src);
return storage;
}
Since a lambda has to be evaluated, you can't simply make its type a template argument:
template<typename> void G();
G<decltype([]{})>(); // doesn't work