0

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.

אנונימי
  • 304
  • 2
  • 7
  • 7
    Vector is a hed herring, this wouldn't work even with a single string. You need `const char *` instead of `char *`. – HolyBlackCat Jul 10 '22 at 17:37
  • Does this answer your question? [Why is conversion from string constant to 'char\*' valid in C but invalid in C++](https://stackoverflow.com/questions/20944784/why-is-conversion-from-string-constant-to-char-valid-in-c-but-invalid-in-c) – fabian Jul 10 '22 at 17:40
  • Note: `struct StructName` or a typedef for avoiding this is unnecessary in C++ when using a struct. You only need this in C code. In C++ `void func(struct s st)` and `void func(s st)` have exactly the same meaning. (Similarly you can use `std::vector`) – fabian Jul 10 '22 at 17:43
  • @fabian I saw this answer. it's only answer why, I nee the how – אנונימי Jul 10 '22 at 17:43
  • 1
    @אנונימי Surely the how is obvious, switch to `const char*` or is that not suitable for some other reason? – john Jul 10 '22 at 17:50
  • Then add a const cast or add a function for this, if you want to shorten the code: `consteval char* IEnjoyLivingDangerously(char const* s) { return const_cast(s); } s v { IEnjoyLivingDangerously("foo"), IEnjoyLivingDangerously("foo") };` – fabian Jul 10 '22 at 17:51
  • @john I'm using an external API, so I can't change char* to const char* – אנונימי Jul 10 '22 at 17:53
  • 1
    "I can't change char\* to const char \*" - then you can't use string-literals. They're read-only and thus pointers to said-same are incompatible with `char*`. – WhozCraig Jul 10 '22 at 18:30
  • 1
    @אנונימי If you external API tries to modify the string literals passed to it, then you have a problem. If it does not then you can use casting to convert your string literals to `char*`. – john Jul 10 '22 at 18:33
  • Is it C API? If so, tag the question with the C tag. – n. m. could be an AI Jul 10 '22 at 21:20

3 Answers3

0

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.)

user3188445
  • 4,062
  • 16
  • 26
  • as I mentioned, func() prototype and struct s declaration are am external API, I can't change it. – אנונימי Jul 11 '22 at 11:11
  • Does the code for this library modify the strings? If not, you could convert from `const char *` to `char *` with a `const_cast`. Not the most elegant, but also probably better than adding a bunch of gratuitous memory allocation. – user3188445 Jul 11 '22 at 11:41
0

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.

lorro
  • 10,687
  • 23
  • 36
-1
#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?

pOuU9w4
  • 1
  • 2
  • I know the problem is that I try to assign string literal (const char*) to char*, but what I need is how to solve it – אנונימי Jul 10 '22 at 18:03
  • constructor will do a favor – pOuU9w4 Jul 10 '22 at 18:26
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 11 '22 at 13:30