I have the below a function I use for string concatenation, it takes in a variable length set of arguments. I want to check to make sure each element is
a char*. I was looking into using dymanic_cast but it cannot be used for char*.
How should I go about casting the arg?:
char* Concatenate(int numStrings, ...)
{
vector<char*> stringVectorArray;
va_list vargList;
if (numStrings > 0 && numStrings < MAX_STRING_BUFFER_SIZE)
{
//Store each of the arguments so we can iterate through them later.
va_start(vargList, numStrings);
for (int currIndex = 0; currIndex < numStrings; currIndex++)
{
char* item = (char*)(va_arg(vargList, char*));
if (item == NULL)
{
//Error: One of the parameters is not char*.
va_end(vargList);
return NULL;
}
else
{
stringVectorArray.push_back(item);
}
}
va_end(vargList);
}
return ConcatenateStrings(stringVectorArray);
}
You simply don't know. There is no well-defined way of knowing what the argument types are for a variable argument list.
You have to trust the caller to get it right: in C, use the (char*) notation, in C++ use reinterpret_cast.
The variadic templates of C++11 introduce type safety into variable argument lists.
"cannot be used for char*"
Don't use char* then, use an object which CAN be used with dynamic cast, like std::string or your own class.
Related
I am trying to return a string from a array with 4 hexadecimal values. I have a huge list of hex arrays and need to use it in multiple functions. That's why I want to create a function of comparing the hex arrays and return a string.
int comparehex(byte hex[]){
char * buffer = new char [16];
byte hex1[4] = {0x4C, 0x79, 0x52, 0xA8};
byte hex2[4] = {0xC5, 0x86, 0xA4, 0xB5};
for (byte i = 0; i <=3; i++){
if (hex1[i] != hex[i]){
break;
}
if (i == 3){
return "string";
}
}
return false;
}
The code that I wrote won't even compile:
main.cpp: In function ‘int comparehex()’:
main.cpp:46:14: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
46 | return "string";
| ^~~~~~~~
| |
| const char*
How can I return a string?
Function Declaration general rule:
return_type function_name( parameter list );
in your case:
int comparehex(byte hex[])
return_type : int
function_name : comparehex.
parameters : byte array.
As per declaration, function is supposed to return int.
but as per your code
return "string"; // returns a STRING
}
}
return false; // return a boolean
To return a string output, declare the return_type as std::string as in.
to do that you need to include "string" library.
#include <string>
std::string comparehex(byte hex[])
Although the return of boolean is cast to int (implicit type conversion).it is not a good practice and is considered unsafe.
refer to Implicit type conversion in C for more details.
Here is an example of returning const char* with nullptr as a special type of "sentinel value" to mean "false":
There are a lot of potential things to address here, and we don't really understand your purpose of the function or use-case, but let me just address your immediate code and one viable solution.
There are many potential ways to handle this. Again, here is just one. But, I have to make some assumptions to even answer. Let's assume that:
Your function needs to return either a string literal OR false (or something equivalent to represent this).
This means you will NOT be generating a string at run-time inside the function. You will ONLY return a string literal (meaning: a compile-time-constant string with double quotes around it like this: "some string literal") which is set in your source code and fixed at compile-time. Otherwise, my example may not meet your needs, and would need some changes.
You will never need to return true. Rather, the existence of a string indicates true as well.
In this case:
// 1. Change this:
int comparehex(byte hex[])
// to this:
const char* comparehex(byte hex[])
// 2. change this:
return false;
// to this:
return nullptr;
// now this line is fine too:
return "string";
Now, the function returns either nullptr to indicate false, OR a string literal such as "string".
You'd simply check to see if "false" was intended by checking like this:
const char* str = comparehex(some_hex_array);
if (str == nullptr)
{
// `comparehex()` essentially returned `false`, so do what you need to do here
}
else
{
// str was set to some string, so use its value (ex: print it)
printf("%s\n", str);
}
Final notes:
Again, if you're generating a new string inside the function at run-time, rather than returning a string literal set at compile-time, the above 2 changes are not sufficient.
Also note that the above code is rather "C-like". It is perfectly valid C++, but only one of many ways to handle the above scenario.
And lastly, nullptr here can be considered a type of "sentinel value", which means simply that it is a special value of your return type (const char*) to indicate a special meaning: false in this case. And therefore, by extension, this sentinel value of nullptr also possesses the special meaning of whatever you intend "false" to mean.
Related
For another generic example of returning const char*, see my const char * reset_cause_get_name(reset_cause_t reset_cause) function in my answer in C here: STM32 how to get last reset status. I don't return NULL (the C analogue to C++'s nullptr) for cases where no match is found, but I could. (In my example I set it to "TBD" instead of NULL).
See also: What exactly is nullptr?
In your case, you could add another parameter for passing by reference:
int comparehex(byte hex[], std::string& result_string);
In your code, before returning, set the result_string parameter to the string you want to return.
I was trying to make a setter for name in some class, but I'm having a problem with this.
void Cats::setName(char* s) {
if (this->name != NULL) {
delete[] name;
name = new char[strlen(s + 1)];
strcpy_s(s, strlen(name) + 1, name);
}
}
That's the setter, and I can't write any name for any Cat of mine.
Cats::Cats() {
setName("NoName"); <----- problem here and in other constructors also.
setWeight(0.6);
setAge(0);
}
argument of type "const char *" is incompatible with parameter of type "char *"
How can I make this happen? What am I missing? An explanation would be great.
"NoName" is a const char*. The const means that no one can modify the characters in it. Your function takes a char*, which is a mutable character array. You can't pass constant data to a function whose signature requires mutable data. If you really don't intend to modify the array, then take a const char*
void Cats::setName(const char* s)
or, even better, as Bathesheba pointed out, use std::string, which will manage the memory for you.
void Cats::setName(std::string s)
Using new and delete directly, in modern C++, is generally a code smell and should be avoided. We have std::string for strings, std::array and std::vector for arrays, and std::unique_ptr and std::shared_ptr for owned heap memory. Let your types speak for themselves, and the compiler will take care of memory management for you.
For starters this statement in the setter
name = new char[strlen(s + 1)];
is incorrect. It should look like
name = new char[strlen(s ) + 1];
Nevertheless the function should be defined at least the following way
void Cats::setName( const char *s ) {
if (this->name != nullptr) delete[] name;
size_t n = strlen( s ) + 1;
name = new char[n];
strcpy_s(s, n, name);
}
You are calling the function passing to it a string literal
Cats::Cats() {
setName("NoName"); <----- problem here and in other constructors also.
setWeight(0.6);
setAge(0);
}
In C++ opposite to C string literals have types of constant character arrays. Thus the parameter of the setter shall be declared like
const char *s
and the constructor should look at least like
Cats::Cats() : name( nullptr ) {
setName("NoName");
setWeight(0.6);
setAge(0);
}
provided that in the class definition the data member name is not initialized explicitly with nullptr.
Going from a vector of strings to a vector of char* to a char**, was working when the argument came in as char**, but the conversion seems to have a problem and I'm not able to find the difference.
Is there a better way to do this?
vector<string> args;
/* code that correctly parses args from user input */
pid_t kidpid = fork();
if (kidpid < 0)
{
perror("Internal error: cannot fork.");
return -1;
}
else if (kidpid == 0)
{
// I am the child.
vector<char*>argcs;
for(int i=1;i<args.size();i++)
{
char * temp = new char[args.at(i).length()];
for(int k=0;k<args.at(i).length();k++)
{
temp[k] = args.at(i).at(k);
}
argcs.push_back(temp);
}
char** argv = new char*[argcs.size() + 1];
for (int i = 0; i < argcs.size(); i++)
{
argv[i] = argcs[i];
}
argv[args.size()] = NULL;
execvp(program, args);
return -1;
}
First, there's no point in copying the std::strings if the next thing you are going to do is call execvp.
If the execvp succeeds, then it will never return and the entire memory image will vanish into smoke (or, more accurately, be replaced by a completely new image). In the course of constructing the new image, exec* will copy the argv array (and the environment array) into it. In any event, the std::vector and std::string destructors will never be invoked.
If, on the other hand, the execvp fails, then the argument passed into it will not have been modified. (Posix: "The argv[] and envp[] arrays of pointers and the strings to which those arrays point shall not be modified by a call to one of the exec functions, except as a consequence of replacing the process image.")
In either case, there was no need to copy the character strings. You can use std::string::c_str() to extract a pointer to the underlying C string (as a const char*, but see below).
Second, if you're using C++11 or more recent, std::vector conveniently comes with a data() member function which returns a pointer to the underlying storage. So if you have std::vector<char*> svec, then svec.data() will be the underlying char*[], which is what you want to pass into execvp.
So the problem reduces to creating a std::vector<char*> from a std::vector<std::string>, which is straightforward:
else if (kidpid == 0) {
// I am the child.
std::vector<char*> argc;
// const_cast is needed because execvp prototype wants an
// array of char*, not const char*.
for (auto const& a : args)
argc.emplace_back(const_cast<char*>(a.c_str()));
// NULL terminate
argc.push_back(nullptr);
// The first argument to execvp should be the same as the
// first element in argc, but we'll assume the caller knew
// what they were doing, and that program is a std::string.
execvp(program.c_str(), argc.data());
// It's not clear to me what is returning here, but
// if it is main(), you should return a small positive value
// to indicate an error
return 1;
}
I have written a function which tries to do an auto-allocating sprintf by returning a std::string instead of writing into a user-supplied char*. (Please, no answers recommending iostreams or Boost.Format or friends -- I know they exist, I do use them in other contexts, but there is a requirement for this particular case.)
std::string FormatString(const std::string& format, va_list argList)
{
char smallBuffer[500], *text = smallBuffer;
int length = _countof(smallBuffer);
// MSVC is not C99 conformant, so its vsnprintf returns -1
// on insufficient buffer space
int outputSize = _vsnprintf(text, length, format.c_str(), argList);
while (outputSize < 0 && errno == ERANGE && length > 0)
{
length <<= 1;
if (text != smallBuffer) { delete[] text; }
text = new char[length];
outputSize = _vsnprintf(text, length, format.c_str(), argList);
}
if (outputSize < 0)
{
throw std::runtime_error("Failed to format string.");
}
std::string ret(text);
if (text != smallBuffer)
{
delete[] text;
}
return ret;
}
std::string FormatString(const std::string& format, ...)
{
va_list argList;
va_start(argList, format);
std::string result;
try
{
result = FormatString(format, argList);
}
catch(...)
{
va_end(argList);
throw;
}
va_end(argList);
return result;
}
int _tmain(int argc, _TCHAR* argv[])
{
int foo = 1234;
std::string bar = "BlaBla";
std::cout << FormatString("%i (%s)", foo, bar.c_str()) << std::endl;
return 0;
}
(And yes, I see the irony of piping a C-formatted string to a C++ iostream. This is just test code.)
Unfortunately, using VS2008, it's crashing deep within the bowels of the printf internals, apparently because it's reading the wrong arguments out of the va_list (according to the debugger, after the va_start it's pointing at a four-byte null sequence immediately prior to the "real" first parameter).
Of particular note is that if in the variadic function I change the const std::string& format to just std::string format (ie. pass by value), it works properly; it also does so if I change it to a const char *, of course.
Is this some sort of compiler bug, or is it not legal to use a va_list with reference parameters?
I think you are out of luck if you want to pass a reference. Here is what the C++2011 standard has to say about the subject in 18.10 [support.runtime] paragraph 3:
The restrictions that ISO C places on the second parameter to the va_start() macro in header are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...).230 If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.
I am trying to implement a variadic function. I searched the web and ended up finding out that most examples handle only one type of arguments (for example calculating average of many integers). Im my case the argument type is not fixed. It can either involve char*, int or both at the same time. Here is the code i ended up with :
void insertInto(int dummy, ... ) {
int i = dummy;
va_list marker;
va_start( marker, dummy ); /* Initialize variable arguments. */
while( i != -1 ) {
cout<<"arg "<<i<<endl;
/* Do something with i or c here */
i = va_arg( marker, int);
//c = va_arg( marker, char*);
}
va_end( marker ); /* Reset variable arguments. */
Now this would work okay if i only had to deal with integers but as you see i have a char* c variable in comments which i would like to use in case the argument is a char*.
So the question is, how do I handle the returned value of va_arg without knowing if it is an int or a char* ?
since you're doing c++ there's no need to use untyped C-style variadic function.
you can simply define a chainable method like
class Inserter
{
public:
Inserter& operator()( char const* s )
{
cout << s << endl;
return *this;
}
Inserter& operator()( int i )
{
cout << i << endl;
return *this;
}
};
then use like
Inserter()( "blah" )( 42 )( "duh" )
variant with templated insert operation commonly used for building strings.
cheers & hth.,
So the question is, how do I handle the returned value of va_arg without knowing if it is an int or a char* ?
You can't. There is absolutely no way to determine the type of a variadic argument. You have to specify it somewhere else. That's why functions like printf require you to give the type of the arguments in the format string:
printf("%s %i\n", "Hello", 123); // works
printf("%s %i\n", 123, "Hello"); // also valid (though most modern compiles will emit a warning), but will produce garbage or crash
You need some way of knowing. Consider how printf solves this, with a format string. It's not the only possible approach, but it's a well-known one.
From http://en.wikipedia.org/wiki/Variadic_function#Variadic_functions_in_C.2C_Objective-C.2C_C.2B.2B.2C_and_D:
In some other cases, for example
printf, the number and types of
arguments are figured out from a
format string. In both cases, this
depends on the programmer to actually
supply the correct information.
So, to handle multiple types, the insertInto() need a concept like format string.
You can make the processing conditional on the dummy parameter. I have routines that take an "action" argument and treats the arguments differently based on the action
For example:
int i; char *c;
switch(dummy) {
case 1:
i = va_arg(marker, int);
// do something with i
break;
case 2:
c = va_arg(market, char *);
// do something with c
break;
}