This is a bit weird, but here goes.
I have many hardcoded "tables" that I'm defining as arrays of std::strings or const char *.
So for example:
const char* resp_desc[] = {
"00=Approved",
"01=Declined",
"03=Incorrect User name",
// more values
NULL
};
In some functions these are passed as the table to lookup the description:
const char* lookup(const char* code, const char** table, const char*default="") {
// lookup code is here..
}
my question is, is it possible to call the lookup function without creating the resp_desc array?
The below code was my first attempt, but I get syntax errors around the {} when trying to use it:
const char* desc = lookup("00", {"00=Approved", "01-Invalid Record", NULL})
It doesn't work with the current C++03, but C++0x will allow to initialize i.e. std::vector with
std::vector<std::string>{"00=Approved", "01-Invalid Record"}
Edit: This works with g++ --std=c++0x (gcc --version is 4.4.3)
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
bool contains(string item, vector<string> const& table) {
return find(table.begin(), table.end(), item) != table.end();
}
int main() {
cout << (contains("foo", vector<string>{"foo", "bar"}) ? "found" : "not found") << "\n";
return 0;
}
If you are willing to change your look-up function, you could alternatively use utilities like Boost.Assign:
// alternative lookup function:
std::string lookup(const std::string& code,
const std::vector<std::string>& table,
const std::string& default="");
// example:
const std::string desc = lookup("00", boost::assign::list_of
("00=Approved")("01-Invalid Record"));
or maybe simply something like this:
typedef std::map<std::string,std::string> Table;
std::string lookup(const std::string& code,
const Table& table,
const std::string& default="")
{
Table::iterator it = table.find(code);
return (it != table.end()) ? it->second : default;
}
const std::string desc = lookup("00", boost::assign::map_list_of
("00","Approved")("01","Invalid Record"));
In short, no. C++ doesn't provide array or structure literals, only array or structure initializers. That is, the { yadda, yadda, yadda } syntax only means what you want when it occurs on the right side of sometype name[] =.
The answer is no.
In C++ (and also C), array types in function parameters are automatically converted to pointer types, as your signature for lookup shows:
const char* lookup(const char* code, const char** table, const char *default);
table is a const char ** and so needs a pointer value. To have a pointer, you need an object in memory to point at.
If you had a simple function such as:
void myfunc(int foo);
you can call myfunc(1) and that's fine. The constant expression 1 is a temporary value which doesn't have a location in memory, and myfunc receives the value directly.
If however you call your lookup function:
const char* desc = lookup("00", /* array constant */);
we can ask: what could /* array constant */ possibly be? lookup needs a pointer to an array object which exists somewhere in memory; but a constant expression doesn't have a location in memory (it is not an lvalue or object) and so there can be no pointer which refers to the constant array expression. As a result, such constant expressions do not exist.
(The one exception to this rule of "no constants which decay to pointers" is the string literal: "Hello World". A string literal creates an array in memory with static duration which exists for the lifetime of the program, and its value returned is a const char * pointing to that array. Sadly, the equivalent for array literals does not exist.)
No sir! The curly bracket syntax is for array initialization only. It does not represent a literal array.
Related
I'm using some C Leagacy Code within a C++ project.
On used C function looks like this
void Add_To_log(const * const char pString_1, const * const char pString_2, int number);
Now when I call this Functions from C++ Code like this
foo()
{
Add_To_log("my first string", "my second string", 2);
}
I get a compiler warning ISO C++ Forbids converting string to char.
So to get rid of this i thought of creating a c++ wrapper with string_view to avoid unnecessary coping of my strings
void CPP_Wrapper(const string_view& string1, const string_view& string2, int number)
{
Add_To_log(string1, string2, 2);
}
Now if i understood the reference correctly string_view does not necessarily contain a terminating null character with is essential for all c functions because it does not own the string object. It simply displays it.
However can i assume in my particular case that string1 and string2 are null terminated?
However can i assume in my particular case that string1 and string2 are null terminated?
No. You should not assume that a string view is null terminated. The wrapper function that you suggest is counter productive, if the C function expects a null-terminated string.
On used C function looks like this
void Add_To_log(const * const char pString_1, const * const char pString_2, int number);
That declaration is ill-formed. If you fix it to be something like:
void Add_To_log(const char * const pString_1, const char * const pString_2, int number)
then this call is well-formed:
Add_To_log("my first string", "my second string", 2); // No problem
std::string already has functions to provide a pointer to older C library functions
http://www.cplusplus.com/reference/string/string/data/
These provide a non-owning, read only pointer suitable to most C library functions that need read only access during the function call. I'm assuming the std::string has a greater lifetime than the function call, and that the pointer is used only during the function call. Or as the documentation I linked above states, "The pointer returned may be invalidated by further calls to other member functions that modify the object." (including the destructor obviously)
Also, take care to use c_str() in c++98 builds, as data() doesn't guarantee the terminating null until c++11, as noted in the documentaion link and by eerorika.
#include <stdio.h>
#include <string>
extern "C" {
void legacy_logger(const char * const pstr) {
printf("%s\n", pstr);
}
}
int main()
{
std::string message{ "This is the string." };
legacy_logger(message.data());
}
I have below code line:
const char *values[] = { "I", "We", "You", "We"};
std::set<const char*> setValues;
for( int i = 0; i < 3; i++ ) {
const char *val = values[i];
std::set<const char*>::iterator it = setValues.find( val );
if( it == setValues.end() ) {
setValues.insert( val );
}
else {
cout << "Existing value" << endl;
}
}
With this I am trying to insert non-repeated values in a set, but somehow code is not hitting to print for existing element and duplicate value is getting inserted.
What is wrong here?
The std::set<T>::find uses a default operator < of the type T.
Your type is const char*. This is a pointer to an address in memory so the find method just compares address in memory of given string to addresses in memory of all strings from set. These addresses are different for each string (unless compiler optimizes it out).
You need to tell std::set how to compare strings correctly. I can see that AnatolyS already wrote how to do it in his answer.
You should define less predicate for const char* and pass into the set template to make the set object works correctly with pointers:
struct cstrless {
bool operator()(const char* a, const char* b) const {
return strcmp(a, b) < 0;
}
};
std::set<const char*, cstrless> setValues;
Unless you use a custom comparison function object, std::set uses operator<(const key_type&,key_type&) by default. Two pointers are equal if, and only if they point to the same object.
Here is an example of three objects:
char a[] = "apple";
char b[] = "apple";
const char (&c)[6] = "apple"
First two are arrays, the third is an lvalue reference that is bound to a string literal object that is also an array. Being separate objects, their address is of course also different. So, if you were to write:
setValues.insert(a)
bool is_in_map = setValues.find("apple") != setValues.end();
The value of is_in_map would be false, because the set contains only the address of the string in a, and not the address of the string in the literal - even though the content of the strings are same.
Solution: Don't use operator< to compare pointers to c strings. Use std::strcmp instead. With std::set, this means using a custom comparison object. However, you aren't done with caveats yet. You must still make sure that the strings stay in memory as long as they are pointed to by the keys in the set. For example, this would be a mistake:
char a[] = "apple";
setValues.insert(a);
return setValues; // oops, we returned setValues outside of the scope
// but it contains a pointer to the string that
// is no longer valid outside of this scope
Solution: Take care of scope, or just use std::string.
(This answer plagiarises my own answer about std::map here)
In C++, the type of string literals is const char [N], where N, as std::size_t, is the number of characters plus one (the zero-byte terminator). They reside in static storage and are available from program initialization to termination.
Often, functions taking a constant string doesn't need the interface of std::basic_string or would prefer to avoid dynamic allocation; they may just need, for instance, the string itself and its length. std::basic_string, particularly, has to offer a way to be constructed from the language's native string literals. Such functions offer a variant that takes a C-style string:
void function_that_takes_a_constant_string ( const char * /*const*/ s );
// Array-to-pointer decay happens, and takes away the string's length
function_that_takes_a_constant_string( "Hello, World!" );
As explained in this answer, arrays decay to pointers, but their dimensions are taken away. In the case of string literals, this means that their length, which was known at compile-time, is lost and must be recalculated at runtime by iterating through the pointed memory until a zero-byte is found. This is not optimal.
However, string literals, and, in general, arrays, may be passed as references using template parameter deduction to keep their size:
template<std::size_t N>
void function_that_takes_a_constant_string ( const char (& s)[N] );
// Transparent, and the string's length is kept
function_that_takes_a_constant_string( "Hello, World!" );
The template function could serve as a proxy to another function, the real one, which would take a pointer to the string and its length, so that code exposure was avoided and the length was kept.
// Calling the wrapped function directly would be cumbersome.
// This wrapper is transparent and preserves the string's length.
template<std::size_t N> inline auto
function_that_takes_a_constant_string
( const char (& s)[N] )
{
// `s` decays to a pointer
// `N-1` is the length of the string
return function_that_takes_a_constant_string_private_impl( s , N-1 );
}
// Isn't everyone happy now?
function_that_takes_a_constant_string( "Hello, World!" );
Why isn't this used more broadly? In particular, why doesn't std::basic_string have a constructor with the proposed signature?
Note: I don't know how the proposed parameter is named; if you know how, please, suggest an edition to the question's title.
It's largely historical, in a sense. While you're correct that there's no real reason this can't be done (if you don't want to use your whole buffer, pass a length argument, right?) it's still true that if you have a character array it's usually a buffer not all of which you're using at any one time:
char buf[MAX_LEN];
Since this is usually how they're used, it seems needless or even risky to go to the trouble of adding a new basic_string constructor template for const CharT (&)[N].
The whole thing is pretty borderline though.
The trouble with adding such a templated overload is simple:
It would be used whenever the function is called with a static buffer of char-type, even if the buffer is not as a whole a string, and you really wanted to pass only the initial string (embedded zeroes are far less common than terminating zeroes, and using part of a buffer is very common): Current code rarely contains explicit decay from array to pointer to first element, using a cast or function-call.
Demo-code (On coliru):
#include <stdio.h>
#include <string.h>
auto f(const char* s, size_t n) {
printf("char* size_t %u\n", (unsigned)n);
(void)s;
}
auto f(const char* s) {
printf("char*\n");
return f(s, strlen(s));
}
template<size_t N> inline auto
f( const char (& s)[N] ) {
printf("char[&u]\n");
return f(s, N-1);
}
int main() {
char buffer[] = "Hello World";
f(buffer);
f(+buffer);
buffer[5] = 0;
f(buffer);
f(+buffer);
}
Keep in mind: If you talk about a string in C, it always denotes a 0-terminated string, while in C++ it can also denote a std::string, which is counted.
I believe this is being addressed in C++14 building on user defined string literals
http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s
#include <string>
int main()
{
//no need to write 'using namespace std::literals::string_literals'
using namespace std::string_literals;
std::string s2 = "abc\0\0def"; // forms the string "abc"
std::string s1 = "abc\0\0def"s; // form the string "abc\0\0def"
}
You can create helper class that will fix that without using overload for every function
struct string_view
{
const char* ptr;
size_t size;
template<size_t N>
string_view(const char (&s)[N])
{
ptr = s;
size = N;
}
string_view(const std::string& s)
{
ptr = s.data();
size = s.size() + 1; // for '\0' at end
}
};
void f(string_view);
main()
{
string_view s { "Hello world!" };
f("test");
}
You should expand this class for helper function (like begine and end) to simplify usage in your program.
I made a template which adds the data it is given. If I use it like this, the compiler declares in_1 and in_2 as const char *, and the code doesn't compile.
#include <iostream>
using namespace std;
template <class T>
T addstuff(T part_1, T part_2){
return(part_1+part_2);
}
int main(int argc, char const *argv[])
{
auto in_1="Shut ";
auto in_2="up.";
cout<<addstuff(in_1, in_2)<<endl;
return 0;
}
If I declare in_1 and in_2 std::string, it works like a charm.
Why can't (or doesn't) the compiler declare those strings automatically std::string?
The reason you can't "write" to your auto variable is that it's a const char * or const char [1], because that is the type of any string constant.
The point of auto is to resolve to the simplest possible type which "works" for the type of the assignment. The compiler does not "look forward to see what you are doing with the variable", so it doesn't understand that later on you will want to write into this variable, and use it to store a string, so std::string would make more sense.
You code could be made to work in many different ways, here's one that makes some sense:
std::string default_name = "";
auto name = default_name;
cin >> name;
If you use string literals, auto will work as expected.
In C++14, C++17 or C++20, you can place an s after the quotes, and it will create a std::string instead of a const char* string.
This can be used together with auto to create a std::string:
auto hello = "hello"s;
String literals are not enabled by default. One way of enabling string literals is to place the following at the top of the source file:
#include <string>
using namespace std::string_literals;
As an example, this loop works for std::string (with s added to the string literal), but not for const char* type string literals:
for (auto &x : hello) {
std::cout << "letter: " << x << std::endl;
}
Here is the cppreference page for the ""s operator.
Because string literals have type const char[N+1], not std::string.
This is just a fact of the language.
They could have made it so that auto has a special case for string literals, but that would be inconsistent, surprising and of very little benefit.
auto will declare the variable as the compile-time type of the expression you initialize it to.
String literals are of type const char*, not std::string.
Compiler tell me "incompatibles type in assignments of char* to char[32]"
this is my code:
char* generalOperations[2]={"Off","On"};
void test(){
char value[32];
switch(swapVariable){
case 0:
value=generalOperations[0]; //<==Error HERE!
break;
}
}
[Solved]:
strcpy(value,generalOperations[0]);
Use std::string instead of char* and std::array<T, N> instead of T[N]. Both are type safe (as opposed to memcpy), both are in modern C++ style and both are directly assignable using the assignment operator.
#include <array>
#include <string>
std::array<std::string, 2> generalOperations{"Off", "On"};
void test() {
std::string value;
switch(swapVariable) {
case 0: value = generalOperations[0]; break;
}
}
You can't assign arrays. You can either change the type of value to a char* or copy the content of generalOptions[0] into value. If you are going to copy the content, then you need to ensure that value has enough space to hold the content of the element in generalOperations.
Modifying a string literal is undefined behaviour, by changing the type to const char* the compiler can detect any attempt to modify one of the entries in generalOperations instead of experiencing odd behaviour at runtime:
const char* generalOperations [2]={"Off","On"};
const char* value;
Note you don't have to specify the number of elements in the array if you are initialising it:
const char* generalOperations [] = {"Off","On"};
Or, if this really is C++ you can make value a std::string instead and just assign to it which will copy the generalOperations element.
As C++ appears to really be the language and C++11 features are permitted instead of using a switch you could create a std::map that associates the int values with the std::string:
#include <iostream>
#include <string>
#include <map>
const std::map<int, std::string> generalOperations{ {17, "Off"},
{29, "On" } };
int main()
{
auto e = generalOperations.find(17);
if (e != generalOperations.end())
{
// Do something with e->second.
std::cout << e->second << "\n";
}
return 0;
}
Demo: http://ideone.com/rvFxH.
#include <string.h>
...
strcpy(value, generalOptions[0]);
You cannot assign arrays in C/C++. There are functions do to that for you. If your char array represents a C style string (i.e. a null terminated sequence of characters), then there are more specialist functions for that as well. strcpy is one of those functions.
Your assignment is wrong, since you cannot assign a char * to char array instead of using this assignment you can use strcpy().