How can I simplify the calling of this function? - c++

I've written (and use) my own string formatting function and I'd like to simplify the usage of the function, in a specific way shown below, but I'm unsure how.
Here's the relevant code:
// Object that can hold a copy of every type I want to print.
// Stores the copy in a union, with an enumeration to identify
// the type. any uses C++ constructors, but could also be implemented
// with C99 designated initializers, like so: https://ideone.com/ElQgBV
struct any
{
...
}
// The string format function requires the variable arguments
// to all be of the 'any' type for type safety and (essential for
// my purposes) positional printing.
// Arguments are accessed with a va_list, so essentially
// the variable arguments are treated as an array of any objects.
char* format_function_(const char* fmt, ...);
// I call the above function with this macro that expands the
// variable arguments and adds a default-constructed sentinel
// at the end. The sentinel is used by the function to count
// how many arguments were passed.
#define format(fmt, ...) format_function_(fmt, __VA_ARGS__, any())
// Calling the function like so, via the above macro...
char* str = format("bits:%4b string:%1 %0 int:%3h float:%2.2\n",
any("world"), any("hello"), any(3.14159f), any(42), any((u8)(1<<4)));
// ...returns this string:
// bits:00010000 string:hello world int:0000002A float:3.14
I'd like to be able to call the function like regular *printf style functions...
char* str = format("bits:%4b string:%1 %0 int:%3h float:%2.2\n",
"world", "hello", 3.14159f, 42, (u8)(1<<4));
...with the use of the any object hidden away, possibly behind another macro.
How can I accomplish this?
Edit/Update The positional arguments are essential for my purposes. Any answer that does not preserve this functionality is not a valid answer.

Since the C++11 standard there's something called parameter packs which makes this very simple:
char* format_function(const char* fmt, ...)
{
...
}
template<typename ...T>
char* format(const char* fmt, T... values)
{
return format_function(fmt, any(values)...);
}
...
char* str = format("bits:%4b string:%1 %0 int:%3h float:%2.2\n",
"world", "hello", 3.14159f, 42, (u8)(1<<4));

Maybe you'ld like something like this? (Alert: C++11 code!)
#include <stdio.h>
inline void format() {}
void format(char ch) {
fputc(ch, stdout);
}
void format(int i) {
if(i < 0) {
fputc('-', stdout);
i = -i;
}
int divider = 1;
while(i / divider >= 10)
divider *= 10;
do {
int digit = i / divider;
i -= divider * digit;
divider /= 10;
fputc('0' + digit, stdout);
} while(divider > 0);
}
void format(const char *str) {
fputs(str, stdout);
}
// TODO: Add more 'format()' overloads here!
template<typename FirstArg, typename... OtherArgs>
inline void format(const FirstArg &first, OtherArgs... others) {
format(first);
format(others...);
}
Then, you can simply...
const char *glorifiedIndex(int index) {
switch(index % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
int main(int argc, const char *const argv[]) {
format("Hello, world!\n");
format("My name is ", argv[0], ", and I was given ", argc - 1, " argument", argc != 2 ? "s" : "", ".\n\n");
for(int i = 1; i < argc; i++)
format(i, glorifiedIndex(i), " argument: \"", argv[i], "\"\n");
format("Goodbye, world!\n");
}
This is a more flexible and elegant model, for the following reasons:
Semantically safe.
Type safe.
No <cstdarg> stuff.
No any stuff.
No incredibly badly-designed iostream stuff.
It's too simple to implemet, and I mean too much :). Compare this few lines of code with a typical 3000+ lines long printf.c. The difference is in several orders of magnitude!
You may have nostalgic moments relating with Java and Python.
If you change the type of any expression for whatever reason (i.e, int to unsigned), the function accomodates itself to this.
(Both good and evil) compiler optimizations can kick in easily.
The user of the library may extended the abilities of the format() function by means of overloading it with user-defined types.
This imposibilites the use of dynamic formats (this is intended for obvious security reasons).
This forces you to create special functions for what I call bit-printing, i.e, printing in a machine-parsable way, rather than human-readable as format() did, does, and will do.
You may use overloading features to extend this list yourself :).

Related

Unexpected behavior of va_list when used in multiple functions

In my project I'm making a C class hierarchy similar to, but not the same as Axel-Tobias Schreiner Object-oriented Programming in ansi C see https://www.cs.rit.edu/~ats/books/ooc.pdf. for example.
I'm initializing my object a little bit different than Axel is. I'm running into troubles when I'm passing va_list objects between multiple initializing functions. Say I've an object two, that derives from object one. Then while initializing a two object, I'll need to initialize the one part first, and subsequently initialize the two part.
So I'm having 1 init function that calls the public initializer function with arguments that initialize the one part of the two object and arguments that only initializes the two part that extends one object.
The library I'm creating is quite large, but I've extracted a mini project from it that demonstrates the same issues:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project (var_args
VERSION "0.0"
LANGUAGES C CXX
)
set(HEADERS "init.h")
set(SOURCES init.c program.c)
add_executable(program ${SOURCES} ${HEADERS})
if (NOT MSVC)
target_compile_options(program PRIVATE -W -Wall -Wextra -pedantic)
else()
target_compile_options(program PRIVATE /W4)
endif()
init.h:
typedef struct _one {
int a;
const char* msg;
} one_t;
/* this struct "derives" from struct _one */
typedef struct _two {
one_t parent;
double pi;
double e;
}two_t;
enum status_t {
STATUS_SUCCES,
STATUS_INVALID_ARGUMENT,
STATUS_ERROR
};
enum init_one_flags {
INIT_ONE_A, // 2nd argument should be of type int
INIT_ONE_MSG, // 3rd argument should be const char*
INIT_ONE_FINISHED, // takes no arugment, but rather tell init1 should be finished.
INIT_ONE_SENTINAL // Keep at the last position.
};
enum init_two_flags {
INIT_TWO_PI = INIT_ONE_SENTINAL, // 2nd arugument should be of type double.
INIT_TWO_E, // 2nd argument shoudl be double.
INIT_TWO_FINISHED, // takes no arugment, but rather tell init2 should be finished.
INIT_TWO_SENTINAL, // for init3...
};
#ifdef __cplusplus
extern "C" {
#endif
int init_two(two_t* two, ...);
//void init_one(one_t* one, ...);
#ifdef __cplusplus
}
#endif
init.c:
#include <stdarg.h>
#include "init.h"
static int priv_init1(one_t* one, va_list list)
{
// use default values;
int a = 0;
const char* msg = "";
int selector, ret = STATUS_SUCCES;
while ((selector = va_arg(list, int)) != INIT_ONE_FINISHED) {
switch (selector) {
case INIT_ONE_A:
a = va_arg(list, int);
break;
case INIT_ONE_MSG:
msg = va_arg(list, const char*);
break;
default:
// unknown argument
return STATUS_INVALID_ARGUMENT;
}
}
one->a = a;
one->msg = msg;
return ret;
}
static int priv_init2(two_t* two, va_list list)
{
double pi = 3.1415, e=2.7128;
int selector, ret = STATUS_SUCCES;
ret = priv_init1((one_t*)two, list);
if (ret)
return ret;
while ((selector = va_arg(list, int)) != INIT_TWO_FINISHED) {
switch (selector) {
case INIT_TWO_PI:
pi = va_arg(list, double);
break;
case INIT_TWO_E:
pi = va_arg(list, double);
break;
default:
return STATUS_INVALID_ARGUMENT;
}
}
two->pi = pi;
two->e = e;
return STATUS_SUCCES;
}
int init_two(two_t* two, ...)
{
int ret;
va_list list;
va_start(list, two);
ret = priv_init2(two, list);
va_end(list);
return ret;
}
program.c:
#include <stdio.h>
#include "init.h"
int main() {
int ret;
two_t two;
ret = init_two(
&two,
INIT_ONE_A, 1,
INIT_ONE_MSG, "Hello, world",
INIT_ONE_FINISHED,
INIT_TWO_PI, 2 * 3.1415,
INIT_TWO_FINISHED
);
if (ret) {
fprintf(stderr, "unable to init two...\n");
printf("a=%d\tmsg=%s\tpi=%lf\te%lf\n",
two.parent.a,
two.parent.msg,
two.pi,
two.e
);
return 1;
}
else {
printf("a=%d\tmsg=%s\tpi=%lf\te%lf\n",
two.parent.a,
two.parent.msg,
two.pi,
two.e
);
return 0;
}
}
Now the problem I'm running into is that the behavior of this code is exactly what I expect on Linux with gcc or clang in debug and release builds. Unfortunately the code fails on Windows with visual studio 17.
So the output of the program should be someting like:
a=1 msg=Hello, world pi=6.283000 e2.712800
And that is exactly what I obtain on Linux with gcc (5.4.0-6)
On windows I get:
a=1 msg=Hello, world pi=jiberish here e2=jiberish here.
And the function init_two does return that the function was successful on Linux and that is is not successful on windows. Also I can see that the part of one_t part of two is initialized successfully, whereas the two_t part is not.
I would be very grateful if someone would point out the issue what is going wrong. Is the va_list is passed by reference on Linux, whereas the va_list is passed by value on windows? Are the enum values promoted to int on Linux, whereas they are passed as char on windows?
Finally, I tagged the question as C and C++ because I know the code I demonstrate is C, but I would like it to work with a C++ compiler as well.
The implementation of va_list can differ greatly across compilers.
It's possible, for example, that gcc implements it as a pointer so passing it to another function by value still modifies the underlying structure so that changes in the called function are visible in the calling function, while MSVC implements it as a struct so changes in the calling function are not visible in the caller.
You can get around this by passing a pointer to your initial va_list instance to all the functions that need it. Then the internal state will be consistent in all functions.
// use pointer to va_list
static int priv_init1(one_t* one, va_list *list)
{
// use default values;
int a = 0;
const char* msg = "";
int selector, ret = STATUS_SUCCES;
while ((selector = va_arg(*list, int)) != INIT_ONE_FINISHED) {
switch (selector) {
case INIT_ONE_A:
a = va_arg(*list, int);
break;
case INIT_ONE_MSG:
msg = va_arg(*list, const char*);
break;
default:
// unknown argument
return STATUS_INVALID_ARGUMENT;
}
}
one->a = a;
one->msg = msg;
return ret;
}
// use pointer to va_list
static int priv_init2(two_t* two, va_list *list)
{
double pi = 3.1415, e=2.7128;
int selector, ret = STATUS_SUCCES;
ret = priv_init1((one_t*)two, list);
if (ret)
return ret;
while ((selector = va_arg(*list, int)) != INIT_TWO_FINISHED) {
switch (selector) {
case INIT_TWO_PI:
pi = va_arg(*list, double);
break;
case INIT_TWO_E:
pi = va_arg(*list, double);
break;
default:
return STATUS_INVALID_ARGUMENT;
}
}
two->pi = pi;
two->e = e;
return STATUS_SUCCES;
}
int init_two(two_t* two, ...)
{
int ret;
va_list list;
va_start(list, two);
ret = priv_init2(two, &list); // pass pointer
va_end(list);
return ret;
}
This usage is explicitly mentioned in section 7.16p3 of the C standard:
The type declared is
va_list
which is a complete object type suitable for holding
information needed by the macros va_start , va_arg , va_end , and
va_copy . If access to the varying arguments is desired, the
called function shall declare an object (generally referred to
as ap in this subclause) having type va_list.
The object ap may be passed as an argument to another
function; if that function invokes the va_arg macro with
parameter ap , the value of ap in the calling function is
indeterminate and shall be passed to the va_end macro prior to
any further reference to ap. 253)
253) It is permitted to create a pointer to a va_list and
pass that pointer to another function, in which case the
original function may make further use of the original list after the
other function returns.
This should work with any compiler:
static int priv_init1(one_t* one, va_list* list)
{
// use default values;
int a = 0;
const char* msg = "";
int selector, ret = STATUS_SUCCES;
while ((selector = va_arg(*list, int)) != INIT_ONE_FINISHED) {
switch (selector) {
case INIT_ONE_A:
a = va_arg(*list, int);
break;
case INIT_ONE_MSG:
msg = va_arg(*list, const char*);
break;
default:
// unknown argument
return STATUS_INVALID_ARGUMENT;
}
}
one->a = a;
one->msg = msg;
return ret;
}
static int priv_init2(two_t* two, va_list list)
{
double pi = 3.1415, e=2.7128;
int selector, ret = STATUS_SUCCES;
ret = priv_init1((one_t*)two, &list);
if (ret)
return ret;
while ((selector = va_arg(list, int)) != INIT_TWO_FINISHED) {
switch (selector) {
case INIT_TWO_PI:
pi = va_arg(list, double);
break;
case INIT_TWO_E:
pi = va_arg(list, double);
break;
default:
return STATUS_INVALID_ARGUMENT;
}
}
two->pi = pi;
two->e = e;
return STATUS_SUCCES;
}
Anyway your design that va_list is increased by two diffrent functions looks very fragile. I wouldn't do that.
Also you API design requires specific and complex sequnce of arguments which compilers do not recognize. This is so bug prone that I would not pass this in code review.
Since this looks like XY problem. I recommend creating another question about your X problem (how to design API which does what you need).
As I understand it, the issue revolves around the fact that your implementation of inheritance requires the initialization function for a subclass to call the initialization function(s) of its superclass(es). This is combined with the fact that the user-facing initialization functions are declared as varargs functions. To make this work, the varargs functions serve as front ends for internal functions that accept the variable arguments via single parameters of type va_list.
This can be made to work in standard C, but you must observe some restrictions. Among those:
Your varargs functions must each initialize their va_list via the va_start macro, exactly once.
Your functions may pass objects of type va_list to other functions, but the caller may not afterward use that object unless they can be confident that the called function did not use either va_arg nor va_end on it, or unless they themselves first use va_end and then va_start on it.
Every use of va_start or va_copy must be paired with a corresponding use of va_end on the same va_list
Of course, it is up to you to ensure that the correct data types are specified to each use of va_arg.
You violate the second requirement by passing a va_list from one internal initialization function to another, and then afterward using it.
You should be able to work around this by passing pointers to the va_list and accessing it indirectly, so that all the functions involved use the same object. This will enable the state of the va_list to be shared, so that, for example, the initialization function for the topmost class can effectively consume the arguments intended for it, without its subclasses needing to know their number or types. That would take a form something like this:
static int priv_init1(one_t* one, va_list *list) {
while ((selector = va_arg((*list), int)) != INIT_ONE_FINISHED) {
// ...
}
// ...
return ret;
}
static int priv_init2(two_t* two, va_list *list) {
int ret = priv_init1((one_t*)two, list);
// ...
while ((selector = va_arg((*list), int)) != INIT_TWO_FINISHED) {
// ...
}
// ...
return STATUS_SUCCES;
}
int init_two(two_t* two, ...) {
int ret;
va_list list;
va_start(list, two);
ret = priv_init2(two, &list);
va_end(list);
return ret;
}

how to get compilation warnings like in printf inside class member function

I want to write logger which would print logs to console using printf function.
Let's assume that I have following piece of code:
class Logger {
public:
Logger(std::string header = "") : header_(header) {}
template<class ...Args>
void LogInfo(const char* message, Args... args);
private:
std::string header_;
};
template<class ...Args>
void Logger::LogInfo(const char* message, Args... args) {
printf(message, args...);
}
This logs well but problem is when I call:
const char* s = "Monty Python";
Logger logger("[Header]");
logger.LogInfo("%d", s);
logger prints pointer-value without any warning, while printf call causes error (with my compilation flags)
error: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘const char*’ [-Werror=format=]
printf("%d", s);
The point of whole code is that I want to get this error during LogInfo call.
How can I do such a thing?
Falling back to printf function family and format strings brings you back quite a lot of issues concerning type safety You might be better off with modern C++ streaming mechanism. Sure, one wouldn't really want to log like this:
logger << "first: " << x << "second: " << y << commit;
So coming up with some alternative approach avoiding the problem with need for appropriate format string parameters; idea is the following:
arguments are inserted one after another at designated locations in the format string
insertion locations are identified by the character pattern %#
pattern %## suppresses argument insertion and is replaced with the insertion pattern as string
Disadvantage: we have to do the parsing ourselves:
void logInfo(char const* message)
{
char const* m = message;
while((m = strchr(m, '%')))
{
if(*++m == '#')
{
if(*++m != '#')
{
std::cout.write(message, m - message - 2);
std::cout << "<missing argument>";
}
else
{
std::cout.write(message, m - message);
++m;
}
message = m;
}
}
std::cout << message << std::endl;
}
template<typename A, typename ... AA>
void logInfo(char const* message, A a, AA ... aa)
{
char const* m = message;
while((m = strchr(m, '%')))
{
if(*++m == '#')
{
if(*++m != '#')
{
std::cout.write(message, m - message - 2);
std::cout << a;
return logInfo(m, aa...);
}
std::cout.write(message, m - message);
message = ++m;
}
}
std::cout << message << std::endl;
}
Sure, there is quite some common code yet, leaving to you to optimise, it's just for the idea...
Worked fine with the following examples:
logInfo("t1");
logInfo("t2", 7);
logInfo("t3: %#", 12);
logInfo("t4: %#%##", 10);
logInfo("t5: %#%%#", 12);
logInfo("t6: %#% baz", 10);
logInfo("t7 1: %# 2: %# 3: %#", 10, 12);
You might add further formatting options such as minimal output width, fill characters, precision, ... – just as printf provides as well...
Sure, this answer does not match exactly your question ("how to produce a warning"), instead, it simply makes the warning obsolete...
Ok, I hoped someone else said it, but I guess I'll be the one to bring macros...
#define LogInfo(logger, format, ...) printf("%s " format, logger.header().c_str(), __VA_ARGS__);
In order to illustrate what can be achieved, I assumed you wanted to add the logger header at each line. This is just an example.
You'd use it that way:
#include <cstdlib>
#include <string>
#include <iostream>
class Logger {
public:
Logger(std::string header = "") : header_(header) {}
std::string const& header() const { return header_; }
private:
std::string header_;
};
#define LogInfo(logger, format, ...) printf("%s " format, logger.header().c_str(), __VA_ARGS__);
int main()
{
const char* s = "Monty Python";
Logger logger("[Header]");
//LogInfo(logger, "%d", s); // error: format '%d' expects argument of type 'int', but argument 3 has type 'const char*' [-Werror=format=]
LogInfo(logger, "%s", s); // [Header] Monty Python
}
Demo: http://coliru.stacked-crooked.com/a/ad698776f2b0ed4f
As pointed out in comments the printf format errors can be used through the format attribute. But you have to loose the vardiag templates for that or add another level of indirection from the vardiac template function to a simple C vardiac function.
The format specifier is implicit in gcc (and other compilers) definition of printf and explicit for many other printf like functions. e.g.
extern int vsnprintf (char *__restrict __s, size_t __maxlen,
const char *__restrict __format, _G_va_list __arg)
__THROWNL __attribute__ ((__format__ (__printf__, 3, 0)));
Because of the attribute the vsnprintf will give the same warnings as a plain printf does. See the linked docs for how to specify the format attribuet for your function (after loosing the vardiac template). Note: Conversion to a plain vardiac function means you have to call vprintf using the varargs macros of the compiler.

Function which takes variadic function as parameter

I'm trying to create function which takes variadic function as parameter, but I can't figure out how to do it in a proper way. This is my try:
#include <iostream>
#include <cstdarg>
using namespace std;
int add(int args, ...) {
int res = 0;
va_list ap;
va_start(ap, args);
for(int i=0; i < args; ++i)
res += va_arg(ap, int);
va_end(ap);
return res;
}
int func(int (*func_arg)(int args, ...)) {
return func_arg; //error
}
int main() {
cout << add(2, 1, 2) << endl; //working
cout << func(add(2, 1, 2)); //error
return 0;
}
This here...
func(add(2, 1, 2))
... calls the function add and tries to pass the result into func. That's making a pointer out of an integer without a cast, etc. So you get an error.
To pass the function itself (after the suitable function to pointer conversion) you'd do this:
func(add)
However, your implementation of func tries to return an integer by converting the function pointer. I strongly suspect that's not what you want. If you intend to call the variadic function, you can do this:
int func(int (*func_arg)(int args, ...)) {
return func_arg(2, 1, 2);
}
But that of course hard-codes the arguments. You'd need to work out a slight design change if you wish to allow passing arguments as well.
Having said all that, if you wish to support callers of func passing in anything that may return an integer, you can use the library type std::function to accomplish that. Then your function will become something like this:
int func(std::function<int()> func_arg) {
return func_arg(); //Invoke the callable
}
And the call site will be able to adapt to it with a simple lambda:
func([]{ return add(2, 1, 2); })
Which will come together into the effect you seem intent on achieving.

How to printf() a user class?

The program fails while compiling the code. Compiler points to printf("Version = '%s'\n", gABXVER). I guess that I actually can't write gABXVER = "V1R1", but I don't have any other idea.
class CISPFVar_BINSTR : public CISPFVar
{
protected:
char* m_pBuffer;
long m_bDefined;
public:
...
void Initialize(char* szName, long lSize, int bDefineVar = 1)
{
Uninitialize();
ZStrToCharArray(szName, m_cName, 8);
m_Size = lSize+1;
m_pBuffer = (char*)malloc(m_Size);
m_pBuffer[0] = 0;
if (bDefineVar)
ISPLINK(__VDEFINE, m_cName, m_pBuffer, __BINSTR, &m_Size);
m_bDefined = bDefineVar;
}
...
};
CISPFVar_BINSTR gABXVER;
char szLoadLibraryPath[50];
int main(
int argc,
char* argv[])
{
if (argc > 1)
if (argv[1]) strcpy(szLoadLibraryPath, argv[1]);
gABXVER.Initialize("ABXVER",4);
gABXVER = "V1R1";
printf("Version = '%s'\n", gABXVER);
return 0;
};
When you use %s in printf family of functions, the corresponding argument type needs to be const char* or something that can be converted to const char*. The argument you are using is not such a type. Perhaps you meant to use:
printf("Version = '%s'\n", gABXVER.m_pBuffer);
The compiler should compile just fine (with possible warnings for printf) because printf doesn't care what you pass to it (beyond the first parameter) or whether it matches the format string. Modern compilers or error checking progs like lint will issue a warning if the params obviously don't match, and if you have a setting "treat warnings as errors", the prog may fail to compile.
That said, CISPFVar_BINSTR needs a public copy constructor if you want to pass it as a parameter by value to a function (because at least semantically a copy will be made). Does it have one? As others remarked it's customary to help your helpers by providing any information you have. Here we are badly missing the compiler errors. (You can edit your post at any time.)
I could imagine that the class has a conversion to char* or std::string, so it may suffice to try either printf("Version = '%s'\n", (char *)gABXVER) or printf("Version = '%s'\n", (std::string(gABXVER)).c_str() ).
You can only printf things that have format specifiers designed specifically for them. There is no format specifier that accepts a value of class type, so you cannot printf one directly.
The best thing you can do is explicitly convert your object to a const char* and pass the result to printf.
In c++ you can use many techniques to implement things like streaming operators
#include <iostream>
class Whatever
{
int value = 42;
public:
int Get() const {
return value;
}
friend std::ostream& operator<<(std::ostream&, Whatever const&);
};
std::ostream& operator<<(std::ostream& os, Whatever const& what) {
os << what.Get();
return os;
}
int main() {
Whatever x;
std::cout << x << std::endl;
}
printf is unsafe
In effect, you're doing serialization of your object into a readable string.

C++11 Function That Only Accepts String Literals?

I want to write a C++11 function that will only accept string literals as a parameter:
void f(const char* s) { static_assert(s is a string literal); ... }
That is:
f("foo"); // OK
char c = ...;
f(&c); // ERROR: Doesn't compile
string s = ...;
f(s.c_str()); // ERROR: Doesn't compile
etc
Is there anyway to implement this? The signature of the function is open to changes, as is adding the use of macros or any other language feature.
If this is not possible what is the closest approximation? (Can user-defined literals help in anyway?)
If not is there a platform specific way in GCC 4.7 / Linux ?
I think the closest you are going to get is this
template<int N>
void f(const char (&str)[N]){
...
}
It will compile with literals and arrays but not pointers.
An alternative might be to make a GCC extension to check at compile time that your particular function is only called with a literal string.
You could use MELT to extend GCC. MELT is a high-level domain specific language to extend the GCC compiler, and is very well suited for the kind of check you want.
Basically, you would add a new pass inside GCC and code that pass in MELT which would find every gimple which is a call to your function and check that the argument is indeed a literal string. The ex06 example on melt-examples should inspire you. Then subscribe to gcc-melt#googlegroups.com and ask your MELT specific questions there.
Of course, this is not a foolproof approach: the function could be called indirectly thru pointers, and it could e.g. have a partial literal string, e.g. f("hello world I am here"+(i%4)) is conceptually a call with some literal string (e.g. in .rodata segment), but not in the generated code nor in the gimple.
I use this :
// these are used to force constant, literal strings in sqfish binding names
// which allows to store/copy just the pointer without having to manage
// allocations and memory copies
struct _literalstring
{
// these functions are just for easy usage... not needed
// the struct can be empty
bool equal(_literalstring const *other) { return !strcmp((const char *)this, (const char *)other); }
bool equal(const char *other) { return !strcmp((const char *)this, other); }
const char *str(void) { return (const char *)this; }
bool empty(void) { return *(const char *)this == 0; }
};
typedef _literalstring *LITSTR;
constexpr LITSTR operator "" _LIT(const char *s, size_t) {
return (LITSTR)s;
}
Then you just declare your function like this :
void myFunc(LITSTR str)
{
printf("%s\n", str->str());
printf("%s\n", (const char *)str);
const char *aVar = str->str();
const char *another = (const char *)str;
}
And you call it like this:
myFunc("some text"_LIT);
If you do something like this:
myFunc("some text");
myFunc(aTextVariable);
you get a compiler error.