initialize const char[] as non-static class member - c++

class foo {
public:
const char name[100];
foo(const char name[]) : name(name) {};
};
int main(){
foo("test");
return 0;
}
Does not compile. How do I initialize const char[] non-static class member?

You have different option, depending on what you want to achieve.
arrays in C++ are strange beasts, they do not behave like most other types, in particular they decay to pointers and do not have copy-constructors (unless wrapped in a structure/class).
foo(const char name[]) does not take an array by value/by copy, it takes a pointer (yes, the syntax is confusing).
Thus name(name) is trying to initialize an array with a pointer.
If this would compile, it would make it super-easy to overflow the stack by accident, as there is no guarantee that the pointer name points to an array that is long at most 100 elements.
Solution 1
Use a more suitable construct - use a string.
From your snippet, it seems you want to store a piece of text (variable named name, initialisation with a string-literal...), so a std::string or other string-like class (even const char*) is a better construct.
class foo {
public:
std::string name;
explicit foo(std::string name_) : name(name_) {};
};
int main(){
foo("test");
}
Solution 2
Use a better array
If you really need to store/copy an array, consider using std::array (since c++11)
#include <array>
class foo {
public:
std::array<char, 100> name;
explicit foo(std::array<char, 100> name_) : name(name_) {};
};
int main(){
foo(std::array<char, 100>{"test"});
}
Solution 3
Pass the array by const-ref.
There are use--cases where you really want to use an array.
In this case you need to pass the value by reference, and copy the content with std::initializer_list (since c++14, but it's possible to emulate in c++11)
#include <utility>
class foo {
template <std::size_t... PACK1>
explicit foo(const char (&name_)[100], std::index_sequence<PACK1...>)
: name{ name_[PACK1]... }
{}
const char name[100];
public:
explicit foo(const char (&name_)[100])
: foo(name_, std::make_index_sequence<100>{})
{}
};
int main(){
const char hello[100] = "hello!";
foo f = foo(hello);
}
const char (&arr)[100] is an array of length 100 passed by const-reference.
As arrays do not have copy-constructors, we need to use index_sequence to initilize all members.
Solution 4
Use pointers and initialize the array in 2 phases.
Passing the array by const-reference means you need to create such a big array beforehand, and that you cannot pass a string literal which length is not exactly 101 (because of terminatig \0).
#include <cstring>
class foo {
const char name[100];
public:
// constructor requires copy... unsure if needs to be so
explicit foo(const char* name_)
{
std::copy(name_, name_ + std::strlen(name_), name);
}
};
int main(){
const char test[100] = "test!";
foo f = foo(test);
}

i would sugest to use a std::string if you are using c++, but if the const char [] is a must, here is the solution, basically you just copy some portion of the shortest string to the array of size 100, leaving unfilled the extra space(ie: name will result in name = ['t','e','s','t','\0',...]
https://www.cplusplus.com/reference/cstring/memcpy/

https://ideone.com/91nC60
#include <iostream>
class foo{
const char *name;
public:
void showme(){
std::cout<<name<<std::endl;
};
foo(const char *_name) : name(_name) {
};
};
int main(){
foo a("test");
a.showme();
return 0;
}
or
#include <iostream>
struct foo{
const char *name;
void showme(){
std::cout<<name<<std::endl;
};
};
int main(){
foo a={
.name="test"
};
a.showme();
return 0;
}

Related

initialize vector of structs of string literals in c++

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?

Is it possible to make a C++ function for a data type that is accessible using a dot operator?

I want to make a function associated to a data type say int that can be accessed using the dot operator such that:
int x = 9;
std::string s = x.ToString();
I know how to do so with std::string and the likes but not the int data type. Is that possible?
No, this is not possible. An int is a primitive type whereas a std::string is a class type that contains methods.
However, you could create your own struct/class in order to implement this functionality. The struct Int type has a constructor which takes in an integer and uses an initializer list :a(value) to assign the internal integer with a given value.
#include <string>
struct Int
{
int a;
Int(int value)
: a(value)
{}
std::string ToString() const { return std::to_string(a); }
};
int main()
{
Int a = 20;
std::string s = a.ToString();
}

Getting an error when I try to assign a char array via the constructor

I want to pass a char array to my constructor and initialize the member variable field, but I get the following error:
error: incompatible types in assignment of 'char*' to 'char [255]'
class C
{
char field[255];
public:
C(char field[255])
{
this->field=field;
}
};
You can't assign/copy an array like that in c++. Instead, you could use std::copy to do a copy of the array, in the constructor:
C(char f[255])
{
std::copy(f, f + 255, field);
}
I suggest using std::string instead of char[255]. Then your class simply becomes:
class C
{
std::string field;
public:
C(std::string field) : field(std::move(field)) {}
};
This way you don't have to worry about the array being large enough.
You can't copy an C array with the assignment operator but C++ provides std::array:
class C
{
std::array<char, 255> field;
public:
C(const std::array<char, 255> &field) : field(field) {}
};
If you can use dynamic memory allocation and the size of the field can also be dynamic you should prefer std::string:
class C
{
std::string field;
public:
C(const std::string &field) : field(field) {}
};
In a function parameter, a char[] (with or without a number specified) is just syntax sugar for a char* pointer. And you can't initialize a char[] array with a char* pointer, hence the compiler error.
For what you are attempting, you must copy the memory that the char* is pointing at, eg:
class C
{
char field[255];
public:
C(char field[255]) // aka C(char *field)
{
memcpy(this->field, field, 255);
// or: std::copy(field, field+255, this->field);
// or: std::copy_n(field, 255, this->field);
}
};
Alternatively, if you want to ensure the caller can only pass in a char[255], pass it by reference instead of by pointer:
class C
{
char field[255];
public:
C(const char (&field)[255])
{
memcpy(this->field, field, 255);
// or: std::copy(field, field+255, this->field);
// or: std::copy_n(field, 255, this->field);
}
};
That being said, you should consider using std::array instead, then you can use operator= assignment like you want:
#include <array>
using Char255Array = std::array<char, 255>;
class C
{
Char255Array field;
public:
C(const Char255Array &field)
{
this->field = field;
}
};
Or better, use the constructor's member initialization list:
#include <array>
using Char255Array = std::array<char, 255>;
class C
{
Char255Array field;
public:
C(const Char255Array &field)
: field(field)
{
}
};
Raw arrays are not recommended to use in c++, you can read about vectors and arrays classes they are more usefull. Although the solution for your problem is you just need to change the type of the attribute field to char*, and it works perfectly.
for more explanation:
in c++, you can assign to a char* type a char array but the reverse is impossible because the pointer is only pointing on the first character.
class C
{
char* field;
public:
C(char field[255])
{
this->field=field;
}
};

Convert C array literal to work in C++ constructor?

I have some C code that I'm trying to convert/wrap into a C++ class. I ran into some C literal arrays(correct me if I'm calling them wrong) and I'm not sure how to initialize them in the C++ constructor since I don't think you can do literals, which I think are compile time specific, to something that is runtime specific.
Should I just explicitly define the array to be of a certain size and just do a strcpy, or some such, of the literal to the array in the constructor?
char sysUpTime[] = {1,3,6,1,2,1,1,3,0};
As an alternative to initializer lists or string manipulation, you could use something like the following (if you really wanted to):
struct Wrapper
{
template <size_t N>
Wrapper(const char(&arr)[N]) : vec(arr, arr+N) { }
vector<char> vec;
};
I've left everything public due to chronic laziness on my part :). You can then initialise like this:
char foo[] = { 1, 2, 3, 4, 5 };
Wrapper bar(foo);
copy(bar.vec.begin(), bar.vec.end(), ostream_iterator<int>(cout, ", "));
Could just use std::string to store OID then initialize it in member initializer list for example:
#include <string>
class Wrapper
{
public:
Wrapper() : sysUpTime("1.3.6.1.2.1.1.3.0") { }
private:
std::string sysUpTime;
};
Or use C++11 std::array
class Wrapper
{
public:
Wrapper() : sysUpTime{{1,3,6,1,2,1,1,3,0}} { }
public:
std::array<char, 10> sysUpTime;
};
The main problem when passing different size c-style arrays to a constructor is that you must pass the size along with it. Here's an example of putting the array into a member vector:
#include <vector>
#include <iostream>
struct Test
{
std::vector<char> sysUpTime;
Test(const char sysUpTime[], size_t size) :
sysUpTime(sysUpTime, sysUpTime + size)
{ }
};
int main()
{
const char sysUpTime[] = {1,2,3,4,5};
Test test(sysUpTime, sizeof(sysUpTime) / sizeof(char));
}
Unfortunately I know of no way to do this without using a dynamic array (vector), aside from using templates which means that you'll get a separate class instantiated for each and every change in the size of your array.

C++ Array Member of Constant Length (Initialisation of)

I have a class that contains an array. I want this array to be set at the length of a constant:
// Entities.h
class Entities
{
private:
const int maxLimit;
int objects[maxLimit];
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
The main problem I'm having is with the constructor. I thought:
// Entities.cpp
Entities::Entities() : maxLimit(50)
{
currentUsage = 0;
cout << "Entities constructed with max of 50" << endl;
}
would have been sufficient...but not so. I don't know if I can use the initialiser list for array initialisation.
How can I initialise the objects array using the maxLimit const? I'm relatively new to classes in C++ but I have experience with Java. I'm mainly testing out this phenomenon of 'constness'.
The array must have a constant length. I mean a length that is the same for all objects of that class. That is because the compiler has to know the size of each object, and it must be the same for all objects of that particular class. So, the following would do it:
class Entities
{
private:
static const int maxLimit = 50;
int objects[maxLimit];
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
And in the cpp file:
const int Entities::maxLimit;
I prefer to use an enumeration for that, because i won't have to define the static in the cpp file then:
class Entities
{
private:
enum { maxLimit = 50 };
int objects[maxLimit];
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
If you want to have a per-object size of the array, then you can use a dynamic array. vector is such one:
class Entities
{
private:
const int maxLimit;
std::vector<int> objects;
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
// Entities.cpp
Entities::Entities(int limit)
: maxLimit(limit), objects(limit), currentUsage(0)
{
cout << "Entities constructed with max of 50" << endl;
}
Best is to do as much initialization in the initialization list as possible.
You can use template argument if you need to set array size at compile time:
template<size_t maxLimit>
class Entities
{
int objects[maxLimit];
public:
Entities() {}
...
};
Entities<1000> inst;
to dynamically allocate the memory you may need to use the 'new' keyword like
objects would be defined like:
int * objects;
inside the constructor you would do:
objects = new int [maxLimit];
edit:
forgot to mention, you'll need to deallocate the array when you're done, probably in the destructor of the class.
delete[] objects;
const ints have to be initialized at declaration. If you don't know the value that it has to be at the time of declaration, you're going to have to adopt a different strategy.
You'll need to create the array in the constructor, while keeping a pointer outside. Is this what you want to do?
In your class :
private:
int maxLimit;
int* objects;
And outside:
Entities::Entities() : maxLimit(50)
{
currentUsage = 0;
cout << "Entities constructed with max of 50" << endl;
objects = new int[maxLimit];
}
Entities::~Entities()
{
delete [] objects;
}
If all objects have the same length, then length can be static. This makes it a constant integral expression allowed as an array bound:
class Entities
{
private:
static const int maxLimit = 50;
int objects[maxLimit];
int currentUsage;
//...
};
Remember that sizeof(Entities) is a valid expression. Each Entities object has that same size.
Use std::vector and you get the expected behaviour. No need to worry about pointers, copies, etc
#include <vector>
class Entities
{
private:
const int limit;
std::vector<int> objects;
public:
Entities(int a_limit)
: limit(a_limit), objects(a_limit)
{ }
void addObject(int identifier)
{
if (objects.size() == limit)
throw whatever;
objects.push_back(identifier);
}
};