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.
Related
I have an API:
void func(struct s st)
{
//do some stuff
}
when struct s define as:
struct s
{
char* str1;
char* str2;
};
Now, I want to call func() multiple times with different pairs of string literals.
So I wanted store my structs in iterable container. for example std::vector:
const std::vector <struct s> sts=
{
{
"str1",
"str2"
},
{
"str3",
"str4"
}
...
};
for(auto& st : sts)
{
func(st);
}
But I get an error:
ISO C++ forbids converting a string constant to ‘char*’.
I know the problem is that I try to assign string literal (const char*) to char*, but how can I fix it?
I know I can implement init function (or define), and call it every time. something like:
s init_struct(const char* str1, const char* str2)
{
char *str1_ = strdup(str1);
char *str2_ = strdup(str2);
return {str1_, str2_};
}
but I want my code simply as possible. also, I can't change func() prototype or struct s declaration.
My question is: what is the fastest and cleanest way to initialize iterable container with the structs?
When I say "fastest" I don't mean to performance (my code doesn't run in real time), but in terms of readability.
In C++, the type of a string literal is an array of const char. Hence, you should define your structure to take a const char *:
struct s
{
const char* str1;
const char* str2;
};
While it would work to use string, this creates unnecessary copies, since the string literals are not going away. (If you want to use a more C++ type, at least use string_view rather than string, but given how simple this case is, I think const char * is just fine.)
First thing to recognize, you'll need to copy somewhere. On some architectures, cstring literals are in a different kind of memory than char*, so to be standard compliant, you need this.
Second trick, a std::initializer_list<T> can be initialized from a class that descends from T. Thus, you can add a class (scons) that has the proper constructor (and descends from s). While in general you cannot convert container<scons> to container<s>, you can still initialize a vector<s> as it'll look for std::initializer_list<s>, which can be constructed this way. Easier to tell by code:
#include <vector>
#include <string.h>
struct s
{
char* str1;
char* str2;
};
// even this could be templated, as arg count can be deduced using structured binding in c++17
struct scons : s
{
scons(const char* s1, const char* s2)
: s{strdup(s1), strdup(s2)} {}
};
int main() {
std::vector<s> v =
{
scons{
"str1",
"str2"
},
{
"str3",
"str4"
},
{
"str5",
"str6"
}
};
}
Note that you only need to specify the new ctor for the first element, the rest is auto-deduced.
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
struct s
{
char* str1;
char* str2;
s(){
}
s(string s1, string s2){
str1=new char[s1.size()+1];
str1[s1.size()]=0;
for(int i(0);i<s1.size();++i)
str1[i]=s1[i];
str2=new char[s2.size()+1];
str2[s2.size()]=0;
for(int i(0);i<s2.size();++i)
str2[i]=s2[i];
}
};
int main(){
const std::vector <struct s> sts=
{
{
"str1",
"str2"
},
{
"str3",
"str4"
}
};
return 0;
}
Is that ok?
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;
}
I have a list of functions that need to be applied to a single string additively. How do I express the "Apply" function.
auto outPutString = inputString
.Apply(Transformation1)
.Apply(Transformation2)
in c++?
The string is the std::string
From C++ 11 onwards, you may also write an Apply function using variadic templates:
template <typename OutputT, typename InputT>
OutputT Apply(const InputT &obj)
{
return obj;
}
template <typename OutputT, typename InputT, typename Func, typename... OtherFuncs>
OutputT Apply(const InputT &obj, Func f, OtherFuncs... other)
{
return Apply<OutputT, decltype(f(obj))>(f(obj), other...);
}
Then you may use this as follows:
auto res = Apply<std::string>(
"Hello",
[](const std::string &str) { return str + " "; }, // Applicator 1
[](const std::string &str) { return str + "World"; } // Applicator 2
);
The result in this case is »Hello World«.
Because the above construction distinguishes between InputT and OutputT, you may "mix" types, as in:
auto res = Apply<size_t>(
"Hello",
[](const std::string &str) { return str + " World"; }, // Applicator 1
[](const std::string &str) { return str.size(); } // Applicator 2
);
This time the result is 11.
Finally, if you really want to use chaining syntax, you could write a class that wraps the initial object and has an Apply method.
Like this:
auto outPutString = Transformation2(Transformation1(inputString));
std::string manipulateString(std::string str) {/* do something */; return result;}
std::string manipulateStringAgain(std::string str) {/* do something else */; return result;}
std::string manipulateMe = "hello";
auto resultString = manipulateString(manipulateStringAgain(manipulateMe));
I'm going to assume when you say "a list of functions", you mean one that varies at runtime. Other answers are better if the list is static.
#include <vector>
#include <string>
#include <functional>
#include <numeric>
std::vector<std::function<std::string(std::string)>> funcs = { Transformation1, Transformation2 }; // or gotten from wherever
auto output = std::accumulate(funcs.begin(), funcs.end(), input, [](auto acc, auto fun){ return fun(acc); });
It is possible in C and C++ as well, to define pointer to a fuction and to create vector of pointers to functions. Later, you can invoke functions inside a loop with desired arguments. Please let me know if you are interested for details.
If you would like to keep the order, create some wrapping class and put your manipulation functions in there. For example:
#include <iostream>
#include <string>
using namespace std;
class StringManipulator
{
public:
StringManipulator(std::string str) : str(str) {}
operator std::string() {return str;}
StringManipulator& doSomething() {str += "1"; return *this;}
StringManipulator& doSomethingElse() {str += "2"; return *this;}
private:
std::string str;
};
int main() {
std::string result = StringManipulator("0").doSomething().doSomethingElse();
std::cout << result;
return 0;
}
Output is 012.
operator std::string ensures implicit conversion.
#include <vector>
#include <iostream>
// three funcitons with a string as the parameter
int funca(std::string& str)
{
std::cout << "funca:" << str << std::endl;
return 1;
}
int funcb(std::string& str)
{
std::cout << "funcb:" << str << std::endl;
return 2;
}
int funcd(std::string& str)
{
std::cout << "funcd:" << str << std::endl;
return 3;
}
int main()
{
// definition of the string
std::string str = "the string";
// declare vector of pointers to function returning an int and receiving a string as a parameter:
std::vector< int(*)(std::string&)> pf;
// load func pointers to vector:
pf.push_back(&funca);
pf.push_back(&funcb);
pf.push_back(&funcd);
//declare vector iterator:
std::vector<int (*)(std::string&)>::iterator it;
// iterate vector of func pointers:
for (it = pf.begin() ; it != pf.end(); ++it)
{
// function call using pointers and passing parameter str
// you can get return value as from 'normal' function
int ret = (*it)(str);
std::cout << "function returns:" << ret << std::endl;
}
}
/*
compiled and executed on ubuntu 18.04, output:
funca:the string
function returns:1
funcb:the string
function returns:2
funcd:the string
function returns:3
*/
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
Consider the following code:
#include <boost/range.hpp>
#include <boost/range/any_range.hpp>
#include <boost/range/join.hpp>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <list>
struct TestData {
TestData() : m_strMem01("test"), m_intMem02(42), m_boolMem03(true) {}
std::string m_strMem01;
int m_intMem02;
bool m_boolMem03;
};
struct IntComp {
bool operator()(const TestData &s, int i) { return s.m_intMem02 < i; }
bool operator()(int i, const TestData &s) { return i < s.m_intMem02; }
bool operator()(const TestData &i, const TestData &s) {
return i.m_intMem02 < s.m_intMem02;
}
};
struct StrComp {
bool operator()(const TestData &s, const std::string &str) {
return s.m_strMem01 < str;
}
bool operator()(const std::string &str, const TestData &s) {
return str < s.m_strMem01;
}
bool operator()(const TestData &i, const TestData &s) {
return i.m_strMem01 < s.m_strMem01;
}
};
typedef boost::any_range<TestData, boost::forward_traversal_tag,
const TestData &, std::ptrdiff_t> TestRange;
std::vector<TestData> vecData(10);
std::list<TestData> listData(20);
TestRange foo() {
TestRange retVal;
auto tmp1 = std::equal_range(vecData.cbegin(), vecData.cend(), 42, IntComp());
retVal = boost::join(retVal, tmp1);
auto tmp2 =
std::equal_range(listData.cbegin(), listData.cend(), "test", StrComp());
retVal = boost::join(retVal, tmp2);
return retVal;
}
int main(int argc, char *argv[]) {
auto res = foo();
for (auto a : res) {
std::cout << a.m_strMem01 << std::endl;
}
//std::cout << res[4].m_intMem02 << std::endl;
}
If you uncomment the last line the code fails since distance_to not implemented for any_forward_iterator_interface. I'm not sure what exactly I'm missing here, like implementing operator[] or distance_to but for what? My own version traversal tag? And why it doesn't work in the first place?
Coliru version
I would say the answer depends on your performance needs and your laziness when it comes to implementing a new iterator abstraction. The core reason for your [] operator not working is the fact that std::list<...> does not provide a random access traversal iterator. If you would have chosen a container that provides such an iterator. You any_range<...> could have taken the random_access_traversal_tag and everything would be fine.
I think it's fair to say that it is not such a big deal to implement a random access iterator on top of a list by simply encapsulating the current index and count forward and backward within the list whenever a specific position is meant to be accessed, but it's clearly against the nature of the list performance-wise.
Is there a good reason to hold one of the collection in a list ?
Is there a good reason to access the resulting any_range by random ?
Is it worth the effort to provide a inefficient random access interface for std::list ?
Of course any_iterator (which underlies the any_range implementation) doesn't gratuitously emulate RandomAccess iterators for any odd iterator you pass.
If you want that, just make an iterator adaptor that does this (making it very slow to random access elements in a list - so don't do this).