C++ Extract std::string from a variable-length argument list - c++

Hey everyone! I'm trying to make a simple copy of sprintf that returns the formatted string, but I am coming into a small snag...
Apparently, using a variable-length argument list you cannot pass a std::string instance.
I already have the parser working properly with int, double, float, char, const char*, char*... I have yet to get strings to work. :\
In case you're wondering, this is the compile error I get: /root/learncpp/StringFormat/main.cpp:8: warning: cannot pass objects of non-POD type 'struct std::string' through '...'; call will abort at runtime
The main reason I'm doing this is so that I can have convenient formatting without having to rely on 3rd party libraries, yet still not have to append ".c_str()" to every string instance I use.
Help with this would be appreciated. Perhaps there's a different version of variable-length argument lists made specifically for C++?
EDIT: I have just realized, if you pass a pointer to a string (i.e. using the & prefix) it works well. All you have to do is dereference a string pointer in the custom sprintf, while passing the address of the std::string!
Still, it would be nice to see if there's any way to support string directly through variable-length argument lists. Thanks!

No -- as the compiler said, you're only allowed to pass objects of POD type to a variadic function.
What you normally want to do is eliminate using a variadic function in the first place, such as by using an iostream instead of something like printf (or a stringstream instead of sprintf).

Related

Avoiding the func(char *) api on embedded

Note:
I heavily changed my question to be more specific, but I will keep the old question at end of the post, in case it is useful to anyone.
New Question
I am developing an embedded application which uses the following types to represent strings :
string literals(null terminated by default)
std::array<char,size> (not null terminated)
std::string_view
I would like to have a function that accepts all of them in a uniform way. The only problem is that if the input is a string literal I will have to count the size with strlen that in both other cases doesn't work but if I use size it will not work on case 1.
Should I use a variant like so: std::variant<const char *,std::span<char>> ? Would that be heavy by forcing myself to use std::visit ? Would that thing even match correctly all the different representations of strings?
Old Question
Disclaimer when I refer to "string" in the following context I don't mean an std::string but just an abstract way to say alphanumeric series.
Most of the cases when I have to deal with strings in c++ I use something like void func(const std::string &); or without the const and the reference at some cases.Now on an embedded app I don't have access to std::string and I tried to use std::string_view the problem is that std::string_view when constructed from a non literal sometimes is not null terminated
Edit: I changed the question a bit as the comments implied some very helphull hints .
So even though y has a size in the example below:
std::array<char,5> x{"aa"} ;
std::string_view y(x.data());
I can't use y with a c api like printf(%s,y.data()) that is based on null termination
#include <array>
#include <string_view>
#include "stdio.h"
int main(){
std::array<char,5> x{"aaa"};
std::string_view y(x.data());
printf("%s",x);
}
To summarize:
What can I do to implement a stack allocated string that implicitly gets a static size at its constructors (from null terminated strings,string literals, string_views and std::arrays) and it is movable (or cheap copyable)?
What would be the underlying type of my class? What would be the speed costs in comparison with the underlying type?
I think that you are looking at two largely and three subtly different semantics of char*.
Yes, all of them point at char but the type-specific info on how to determine the length is not carried by that. Even in the ancient ancestor of C++ (not saying C...) a pointer to char was not always the same. Already there pointers to terminated and non-terminated sequences of characters could not be mixed.
In C++ the tool of overloading a function exists and it seems to be the obvious solution for your problem. You can still implement that efficiently with only one (helper) function doing the actual work, based on an explicit size information in a second parameter.
Overload the function which is "visible" on the API, with three versions for the three types. Have it determine the length in the appropriate way, then call the single helper function, providing that length.

C++ - What type of parameters should I accept? (When should we cast?)

I'm using mysql++, where we need to form queries and thus there's necessity to do a type check before we send it to mysql++ wrapper and the database. In some cases, we need something like uid which is basically an int type. However, I can also accept a std::string and check if I can turn it into an integer number. In some interpret languages, there is no such problem because all types are stored as string and you are able to easily parse and convert them with int(str). The problem is when should I prefer a conversion? There might be some convenience in converting from string to other types: I can use methods of string and bound some checks. But what are the cons?
Almost never. The reason of having types is to specify the intent or the way a method should be used.
If at the end of your call chain you require an int, that should be reflected in your method signatures.
There is also a subtle difference between casting and the int(str). The second one is not a cast but involves some kind of parsing of the string to specify whether the string actually represents a number.
In C++, you'll have to try to parse the string to an int, check if the conversion was successful and continue from there. Why pay for that when you know that you need an int ?
If somehow you are getting strings, you can try and parse them before calling the function. I think it's cleaner this way.

Working With Variadic Functions C++

I am using variadic function in one of my programs. Here is definition of it -
std::string GetMessage(const char *format,...)
Now I would like to give extra feasibility to user to provide plain string is he wants to.
Like - GetMessage("Here is my message")
I facing trouble while processing such inputs , inputs with variable is working fine.
I tried with
val_list vl;
va_start(vl,format);
if(strlen(vl) > 1) // TO check if va list has any arguments.
But it has very in-consistent behavior.
So can somebody please suggest me better way to know if variadic function has arguments OR No arguments?
The caller has to actively pass some values that let the called var/stdarg-using function know where the arguments end. Common techniques include:
passing a sentinel value, such as a trailing NULL
passing a count
passing a format string where embedded content (e.g. %-prefixed formats for printf() et al) imply the needed number of arguments
You can sometimes facilitate reliable use of your variadic function by calling them via macros that inject a sentinel, and some compilers let you piggy-back on their printf() style argument checks (e.g. GCC).

Initialise System::String^ with literal but not string::c_str() or char[]

I've recently started learning about .net and the Windows API and I'm currently writting a class which talks the serial port.
In order to write a line to the serial port, I use the WriteLine function which takes a parameter, System::String^. I would like to know if it is possible to initialise the System::String^ from a std::string.
I've noticed that I can do the following:
serialPort->WriteLine("stuff");
but I cannot do:
std::string data = "stuff";
serialPort->WriteLine(data.c_str());
also, I cannot do:
char data[] = "stuff";
serialPort->WriteLine(data);
nor
char* data = "stuff";
serialPort->WriteLine(data);
Could someone let me know what's going on here? Is VS somehow converting the literal to it's own thing when I pass it in?
Lastly, I'm not sure if I've selected the correct tags for this as I don't really know what this falls under.
You can always call functions with string literals because the compiler automatically converts that to a value of the appropriate string type.
The problem here is that you're trying to mix the .NET Framework stuff (System::String) with the standard C++ string type (std::string), and they simply don't mix well. The .NET functions that accept a parameter of type System::String are not written or overloaded to accept a parameter of type std::string. If you're working with the .NET Framework, then you're using C++/CLI, and you're generally expected to use the .NET string type, not the C++ string type.
Analogously, standard C++ functions are expecting std::string and don't know anything about the .NET Framework string type. So if you're expecting to call functions from the C++ standard library, you should be using its string type.
C-style strings (char[]) are the old fallback, not something you really ever want to use when writing C++ code. std::string is always a better option, and it even provides the c_str() method that you already know about to use when calling an API function that expects a char array. But like the other two string types, this one isn't inter-convertible either.
One particularly large problem that you'll run into is that std::string and char[] types accept only "narrow" (non-Unicode) strings, whereas the .NET Framework System::String type works only with wide (Unicode) strings. That throws a further wrench in the mix when it comes to conversion. There's no way it can be as simple as a cast.
But that doesn't mean it's impossible to convert between the types. There's a helpful document on MSDN that explains how to convert between various C++ string types.
In summary, you can convert from C-style strings to System::String by using the appropriately overloaded constructor that accepts a char*:
const char* cString = "Hello, World!";
System::String ^netString = gcnew System::String(cString);
Console::WriteLine(netString);
delete netString;
And then, of course, you can convert from to std::string to System::String by combining the above method with the c_str() method:
std::string stdString("Hello, World!");
System::String ^netString = gcnew System::String(stdString.c_str());
Console::WriteLine(netString);
delete netString;

(Obj) C++: Instantiate (reference to) class from template, access its members?

I'm trying to fix something in some Objective C++ (?!) code. I don't know either of those languages, or any of the relevant APIs or the codebase, so I'm getting stymied left and right.
Say I have:
Vector<char, sizeof 'a'>& sourceData();
sourceData->append('f');
When i try to compile that, I get:
error: request for member 'append' in 'WebCore::sourceData', which is of non-class type 'WTF::Vector<char, 1ul >& ()();
In this case, Vector is WTF::Vector (from WebKit or KDE or something), not STD::Vector. append() very much is supposed to be a member of class generated from this template, as seen in this documentation. It's a Vector. It takes the type the template is templated on.
Now, because I never write programs in Real Man's programming languages, I'm hella confused about the notations for references and pointers and dereferences and where we need them.
I ultimately want a Vector reference, because I want to pass it to another function with the signature:
void foobar(const Vector<char>& in, Vector<char>& out)
I'm guessing the const in the foobar() sig is something I can ignore, meaning 'dont worry, this won't be mangled if you pass it in here'.
I've also tried using .append rather than -> because isn't one of the things of C++ references that you can treat them more like they aren't pointers? Either way, its the same error.
I can't quite follow the error message: it makes it sound like sourceData is of type WTF:Vector<char, 1ul>&, which is what I want. It also looks from the those docs of WTF::Vector that when you make a Vector of something, you get an .append(). But I'm not familiar with templates, either, so I can't really tell i I'm reading that right.
EDIT:
(This is a long followup to Pavel Minaev)
WOW THANKS PROBLEM SOLVED!
I was actually just writing an edit to this post that I semi-figured out your first point after coming across a reference on the web that that line tells the compiler your forward declaring a func called sourceData() that takes no params and returns a Vector of chars. so a "non-class type" in this case means a type that is not an instance of a class. I interpreted that as meaning that the type was not a 'klass', i.e. the type of thing you would expect you could call like .addMethod(functionPointer).
Thanks though! Doing what you suggest makes this work I think. Somehow, I'd gotten it into my head (idk from where) that because the func sig was vector&, I needed to declare those as &'s. Like a stack vs. heap pass issue.
Anyway, that was my REAL problem, because I tried what you'd suggested about but that doesn't initialize the reference. You need to explicitly call the constructor, but then when I put anything in the constructor's args to disambiguate from being a forward decl, it failed with some other error about 'temporary's.
So in a sense, I still don't understand what is going on here fully, but I thank you heartily for fixing my problem. if anyone wants to supply some additional elucidation for the benefit of me and future google people, that would be great.
This:
Vector<char, sizeof 'a'>& sourceData();
has declared a global function which takes no arguments and returns a reference to Vector. The name sourceData is therefore of function type. When you try to access a member of that, it rightfully complains that it's not a class/struct/union, and operator-> is simply inapplicable.
To create an object instead, you should omit the parentheses (they are only required when you have any arguments to pass to the constructor, and must be omitted if there are none):
Vector<char, sizeof 'a'> sourceData;
Then you can call append:
sourceData.append('f');
Note that dot is used rather than -> because you have an object, not a pointer to object.
You do not need to do anything special to pass sourceData to a function that wants a Vector&. Just pass the variable - it will be passed by reference automatically:
foobar(sourceData, targetData);
Dipping your toes in C++ is never much fun. In this case, you've run into a couple of classic mistakes. First, you want to create an instance of Vector on the stack. In this case the empty () is interpreted instead as a declaratiton of a function called sourceData that takes no agruments and returns a reference to a Vector. The compiler is complaining that the resulting function is not a class (it's not). To create an instance of Vector instead, declare the instance without the () and remove the &. The parentheses are only required if you are passing arguments to the instance constructor and must be omitted if there are no arguments.
You want
Vector<char, sizeof 'a'> sourceData;
sourceData.append('f');
Vector<char, sizeof 'a'> outData; //if outData is not instantiated already
foobar(sourceData, outData);
This Wikipedia article gives a decent introduction to C++ references.