I want simplify the code to use the same function for both ANSI and UNICODE string version
tstring formatW(const std::string format, ...);
tstring formatA(const std::wstring format, ...);
in a single parametrized function
tstring format(const tstring format, ...);
However if I use this as, I get an error in line 3
const std::string fmtA = "MouseWheel nFlags %u,zDelta %d, CPoint(%d,%d";
std::string strA = formatA(fmtA, nFlags, zDelta, pt.x, pt.y ); // Ok
std::string strA2 = format (fmtA, nFlags, zDelta, pt.x, pt.y ); // Fail :(
const std::wstring fmtW = L"MouseWheel nFlags %u,zDelta %d, CPoint(%d,%d";
std::wstring strW = formatW(fmtW, nFlags, zDelta, pt.x, pt.y); // Ok
std::wstring strW2 = format (fmtW, nFlags, zDelta, pt.x, pt.y); // Fail
The error I get is this:
error C2664: ~tstring Format(const tstring,..'.)': cannot convert argument 1 from 'const std::string' to 'const tstring'
I am not familiar with parameteized functions.
char and wchar_t (the underyling types of string/wstring) are not compatible (8 and 16 bits) so you cannot combine them inside a runtime-checking environment.
That is why Windows for example has 2 versions for many functions (MessageBoxA, MessageBoxW etc).
Solution: use a class that stores data as UTF-8 (char) then converts them to 16-bit when wchar_t is needed. Or better, vice versa (uses wchar_t internally and converts data to UTF-8 when a char* is needed).
I'm assuming you have good reasons not to just do the Microsoft #ifdef UNICODE thing? It is a bit rubbish, I admit.
You can't check them at runtime, but this is exactly what c++ compile-time overloading was invented for.
So you probably need to do 3 different things:
1) Write 2 function prototypes that give you the behaviour you need
2) Convert them to take a variadic template argument list instead of ...
3) encapsulate it all in a templated class.
1) To declare and implement the two helper functions with the same name, just do that. The compiler will distinguish them based on the first parameter:
tstring format(const std::string& format, ...);
tstring format(const std::wstring& format, ...);
Of course you then end up writing the same boilerplate code twice, something like:
tstring format(const std::string& format, ...) {
va_list format; /// hmm va_list with references - may need a tweak!
va_start(format, __arg);
return vformatA(format, __arg);
}
And it is a pain writing this twice for each method you merge.
2) To use the variadic templates, Declare and implement your functions as:
template <typename... T>
tstring format(const std::string& format, const T& ... t)
{
return formatA(format, t...);
}
Again you need to write this twice. It isn't too complex compared to the c-style variadics, so you might accept that.
3) A third optional step is to write a Helper template class. So the idea of this is that the actual implementation is performed in a helper class, which you only define for string and wide string. To be honest all it does is add bulk and force you to implement both methods.
You could always use templates and limit them to only string and wstring type, so
template <typename T>
T format(const T format, ...);
and later declare two functions that will do what you want according to the type, so
std::string format<std::string>(const std::string format, ...){...}
std::wstring format<std::wstring>(const std::wstring format, ...){...}
Related
I am using Il2CppInspector to generate scaffolding for a Unity game. I am able to convert System.String (app::String in Il2CppInspector) to std::string using the functions provided below.
How would I reverse this process; how do I convert a std::string to System.String?
helpers.cpp
// Helper function to convert Il2CppString to std::string
std::string il2cppi_to_string(Il2CppString* str) {
std::u16string u16(reinterpret_cast<const char16_t*>(str->chars));
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(u16);
}
// Helper function to convert System.String to std::string
std::string il2cppi_to_string(app::String* str) {
return il2cppi_to_string(reinterpret_cast<Il2CppString*>(str));
}
In short, I am looking for a function that takes in a std::string and returns an app::String
// Helper function to convert std::string to System.String
app::String string_to_il2cppi(std::string str) {
// Conversion code here
}
The accepted answer is actually wrong, there is no size parameter and copying stops at the first null byte (0x00) according to the MSDN documentation.
The following code fixes these problems and works correctly:
app::String* string_to_il2cppi(const std::string& string)
{
const auto encoding = (*app::Encoding__TypeInfo)->static_fields->utf8Encoding;
const auto managed_string = app::String_CreateStringFromEncoding((uint8_t*)&string.at(0), string.size(), encoding, nullptr);
return managed_string;
}
A quote from djkaty:
To create a string, you cannot use System.String‘s constructors –
these are redirected to icalls that throw exceptions. Instead, you
should use the internal Mono function String.CreateString. This
function has many overloads accepting various types of pointer and
array; an easy one to use accepts a uint16_t* to a Unicode string and
can be called as follows [...]
Export Il2CppInspector with all namespaces, which will give you access to Marshal_PtrToStringAnsi.
app::String* string_to_il2cppi(std::string str) {
return app::Marshal_PtrToStringAnsi((void*)&str, NULL);
}
Limitation: do not attempt to convert a string with null terminators inside of them example:
std::string test = "Hello\0world";
Use BullyWiiPlaza's solution if this is an issue for you.
In Qt 4 it is possible to automatically cast a QString to a "const char *", e.g. I could pass a QString to a function that expected a "const char *".
void myFunction(const char *parameter);
QString myString;
myFunction(myString); //works in QT4
In Qt 5 I would however get an "error C2440: 'type cast' : cannot convert from 'QString' to 'const char *'" (that is the Visual C++ 2008 compiler, other compilers would throw something similar). If I understand the documentation correctly, it is because the Qt 3 compatibility layer is not included anymore in QT5.
Of course, I could change the function call from
myFunction(myString);
to
myFunction(myString.toLatin1().data());
However, I have a huge code base that compiles fine with Qt 4 and I would really like to have my old code compile with Qt 5 without modifying it. Is there any way to achieve this?
You could create a macro or inline function for your purpose to minimize the changes, but since that would also require a grep alike operation, there is not much difference.
#define QString2charp(myString) myString.toLatin1().data()
or
inline char* QString2charp (const QString &myString)
{
return myString.toLatin1().data();
}
and then:
myFunction(QString2charp(myString));
BUT
of course, in an ideal world, it would be nice if your "myFunction" could get an overload expecting a QString argument.
void myFunction(const char *parameter);
void myFunction(QString parameter);
and then the overload would be implemented like this:
void myFunction(const QString ¶meter)
{
myFunction(myString.toLatin1().data());
}
of course, this would require the constructor being explicit so that no implicit conversion can happen, otherwise the compiler will complain about ambiguity in presence of both when trying to pass const char*, but if you always use QString, it should just work with Qt 5.
This is somewhat equal to rewriting the original function to a different signature expecting QString though because unfortunately the corresponding constructor is not explicit. I imagine that was not changed in Qt 5 for compatibility as it would have broken too much code.
As you can see, you do not need to change anything in your code this way, other than adding a one-liner overload. It is neat, isn't it?
You could either
Change the signature of your function to MyFunction( const QString & parameter ) (of course that works only if you don't need the one that takes char * any more)
Overload MyFunction:
void myFunction(const char *parameter); //this is your normal function
void myFunction( const QString & parameter )
{
char * c_str = nullptr;
<convert parameter to char * in any way that suits you>
myFunction( c_str );
}
If the above is impossible for some reason, the best approach is probably to write a similar wrapper:
void MyFunctionWrapper( const QString & parameter )
{
<same as above>
}
It is no longer possible to cast QString to const char* in >= Qt 5, because QString is UTF-16 and no longer has an entry for caching the result of converting to ASCII.
The way to achieve your aim is
a) Add overloads to the functions expecting const char* which can handle QStrings,
b) Add the explicit conversion code to the call-site, or
c) Go to some char-based string, like std::string.
None of those fulfill your aim of nearly no changes though.
I found this on QT forum
const QByteArray byteArray = mytext = textBox.text().toUtf8();
const char *mytext = byteArray.constData();
This resolved my issue
forward argument list from a c++ class member function to a c function, (a wrapper I am creating)
not sure if it is correct? look in the comment inside argsPrinter
// c++ a class function
void argsPrinter( const char *format , ... ){
//markFile(&mApiObj, format , ...); how to pass forward the ... to the c function
/*
va_list args;
va_start (args, format);
markFile(&mApiObj, format , args);
va_end(args);
*/
}
// c function
void markFile(someCustomApi* a, const char *format , ...)
{
FILE *file= fopen(a->somePath, "a");
if(file)
{
va_list args;
va_start (args, format);
vfprintf(file, format, args);
va_end (args);
fclose(file);
}
//else do nothing
}
Edit
the implementation changed, however, I may consider implementing an aditional function, if forward is not allowed to the ...
as markFile(&mApiObj, format , ...);
You can't. If the library containing markFile doesn't provide a markFileV (similar to vsprintf being the counterpart to sprintf), it's just not possible to do this.
Given that you have the source of the C function, you may be able to change that, though.
On a side note (if you have any influence on the C code), this may be a reduced example, but why is markFile first formatting into a buffer (that is PATH_MAX chars long, of all things! What does that have to do with anything?) and then using fprintf to write out the results? Why not just use vfprintf directly?
It's not possible to forward C-style variadic arguments. That's why both fprintf and vfprintf exist. If you need to forward them, you'll have to create your own version of markFile, something like vmarkFile, which would accept a va_list instead of ....
How about just call your C++ function inside implementation of C function?
Such as your C++ function
void argsPrinter( const char *format , ... ){
//markFile(&mApiObj, format , ...); how to pass forward the ... to the c function
/*
va_list args;
va_start (args, format);
markFile(&mApiObj, format , args);
va_end(args);
*/
}
You can implement your C function by calling it:
void markFile(someCustomApi* a, const char *format , ...)
{
a->argsPrinter(format, ... );
//else do nothing
}
Since it is highly ABI dependent you can not do this easily.
Alternatively you can use libffi. Look for variadic function call in the man page.
I still recommend that you write a function that accept a va_list as parameter like vsprintf and co.
Short version: How can I pass the contents represented by ... in a variable argument function to another function without first parsing it into a va_list?
Long version:
Below are two functions in a class of mine. I would like to draw your attention to the fact that the first four lines of each function are identical. And I have a half dozen other functions in this class with the same first four lines.
void cyclOps::Logger::warn(char* szFile, char* szFunction, int iLine, char* szFormat, ...) {
va_list vaArguments;
va_start(vaArguments, szFormat);
char szOutput[10000];
_vsnprintf_s(szOutput, CYCLOPSSIZEOF(szOutput), _TRUNCATE, szFormat, vaArguments);
this->log("WARNING: %s [%s - %s(%d)]", szOutput, szFile, szFunction, iLine);
}
void cyclOps::Logger::info(char* szFormat, ...) {
va_list vaArguments;
va_start(vaArguments, szFormat);
char szOutput[10000];
_vsnprintf_s(szOutput, CYCLOPSSIZEOF(szOutput), _TRUNCATE, szFormat, vaArguments);
this->log("INFO: %s", szOutput);
}
I would like to put these four identical lines in a single function called summarizeVariableArguments() and call it something like this...
void cyclOps::Logger::info(char* szFormat, ...) {
std::string strOutput = this->summarizeVariableArguments(/* TBD */);
this->log("INFO: %s", strOutput.c_str());
}
...where the contents of strOutput would be the same as the contents of szOutput in the two previous functions. But how do I pass the ... parameter to another function?
You cannot do that portably (or perhaps at compile time, with horrible C++2011 variadic template tricks).
If you want to call at runtime a variadic function, you may want to use the libffi.
Details are operating system, compiler, processor and ABI specific. (but libffi is trying to abstract them).
That's what perfect forwarding is all about + variadic templates.
template<typename ...Args>
void cyclOps::Logger::info(char* szFormat, Args &&...args) {
std::string strOutput = this->summarizeVariableArguments(std::forward<Args>(args)...);
this->log("INFO: %s", strOutput.c_str());
}
You make another function that accepts va_list to do the job like so:
void cyclOps::Logger::vLog(const char* format, va_list args)
{
std::string logMessage = vFormat<10000>(format, args);
// Do what you want with logMessage
}
template <size_t BufferSize>
std::string cyclOps::Logger::vFormat(const char* format, va_list args)
{
char buffer[BufferSize];
vsprintf(buffer, format, args);
return std::string(buffer);
}
I have tested this on MSVC and GCC for my project. All I can say is it works for me.
Here's a working example. This solution works for C++03 and I believe should work with C++11.
What I am trying to get at is a function that generates an instance of a CStringT<> that is appropriate for a given type of character (char or wchar_t).
Here's a contrived example:
#include <atlstr.h>
template <typename CHAR_T>
inline
CStringT< CHAR_T, ATL::StrTraitATL< CHAR_T, ATL::ChTraitsCRT< CHAR_T > > >
AsCString(const CHAR_T * psz)
{
return CStringT< CHAR_T, ATL::StrTraitATL< CHAR_T, ATL::ChTraitsCRT< CHAR_T > > >(psz);
}
I can certainly use the above (it appears to compile), but it would be a lot nicer for readers of my code (and probably for future compatibility) if I could use something like:
ATL::make_cstring(psz);
Is anyone aware of such a utility, or anything like it?
It's already there, use either CString (project setting), CStringA or CStringW. After 17+ years of Unicode and operating systems that are exclusively Unicode, there are very few reasons left to avoid getting the project setting right.
Lets see if I understand it correctly, you want to create a string based on the type of char (char /Wchar_t)
template <typename _T>
CStringT <_T, ATL::StrTraitATL <_T, ATL::ChTraitsCRT <_T > > >
make_cstring (_T* __args)
{
.. create cstring and return it
}
Thank You, everyone who helped out with this.
Here is the answer that most completely answers my needs:
// generates the correct CString type based on char_T
template <typename charT>
struct cstring_type
{
// compile time error if no matching CStringT<> for charT
};
template <>
struct cstring_type<char>
{
typedef CStringA type;
};
template <>
struct cstring_type<wchar_t>
{
typedef CStringW type;
};
#define CSTRINGTYPE(T) typename cstring_type<T>::type
// returns an instance of a CStringA or CStringW based on the given char_T
template <typename charT>
inline CSTRINGTYPE(charT) make_cstring(const charT * psz)
{
return psz;
}
As you can see, it fully generates the correct CString type (either CStringA or CStringW) based entirely upon the template parameter (charT). This version has the added advantage of not trying to explicitly generate a CStringT<...> which depends on further compile time options (such as using CRT, using MFC, etc.). Instead, it results in the system-supplied CStringA or CStringW, which Microsoft maintains, hence giving my code greater flexibility and future-proofing it (as much as can be gained, I expect).
Don't use all caps for non-macro purposes.
Why not just stick it in the ATL namespace and call it make_cstring? (hell, why not just use std::string?)