Macro Expansion Ternary Lookup Table - c

I'm trying to create a lookup macro. Using the ternary operator seems to be a very concise way of doing this. Here's what I have so far:
#define SQL_LOOKUP_TABLE(x) (strncmp(x, "int", strlen(x)) == 0) ? "INTEGER" : SQL_LOOKUP_TABLE1(x)
#define SQL_LOOKUP_TABLE1(x) (strncmp(x, "char", strlen(x)) == 0) ? "TEXT" : SQL_LOOKUP_TABLE2(x)
#define SQL_LOOKUP_TABLE2(x) (strncmp(x, "double", strlen(x)) == 0) ? "REAL" : ""
I want to pass in the type as a string in c, and then get the corresponding SQL type back as a string. It works great when I do something like this:
printf("Ternary test: %s\n", SQL_LOOKUP_TABLE("double")); //output "REAL"
What I really want to do, is take this information and build an entire SQL CRUD statement. The problem comes in when I try to build the string inside of another macro. Something like this does not work:
#define BUILD_A_STRING(x) "CREATE TABLE ( " SQL_LOOKUP_TABLE( x )
I get error:
error C2064: term does not evaluate to a function taking 337 arguments
Quick note, this does work(returns "REAL"):
#define BUILD_A_STRING(x) SQL_LOOKUP_TABLE( x )
Any ideas why I can't call the macro inside of another macro and also build a string?
Edit(at the risk of providing TMI):
This is what I really want to do:
typedef struct {
double yomama;
int x;
char shiboopy[100];
} test_data1;
#define EXPAND_AS_CREATE_STATEMENT(type, element, struct_name) SQL_LOOKUP_TABLE( #type) " " # element ", "
#define test_data1_TABLE(ENTRY) \
ENTRY(double, yomama, test_data1) \
ENTRY(int, x, test_data1) \
ENTRY(char, shiboopy, test_data1)
char* create_stmt = "CREATE TABLE test_data1 (" test_data1_TABLE(EXPAND_AS_CREATE_STATEMENT) ");"; \
Basically use an X macro to define a struct's data types and then expand it out into whatever CRUD statements I might need.

Lexical string concatenation is a preprocessor operation. The ternary operator is a runtime operation. When the strings are attempted to be concatenated, the preprocessor will look for adjacent string literals, but will fail to find any because
"CREATE TABLE ( " SQL_LOOKUP_TABLE( x )
is preprocessed to
"CREATE TABLE ( " (strncmp(x, "int", strlen(x)) == 0) ? "INTEGER" : SQL_LOOKUP_TABLE1(x)
and the preprocessor does not know about strncmp, strlen, or the ternary operator.
To do what you want, you'd have to do the conditional stuff when the preprocessor runs. The C preprocessor is too simplistic for that, though, it would require a more sophisticated preprocessor like m4 for that.
The other, unfavorable way to go would be to do the concatenation at runtime with a little overhead involved, obviously.
I recommend you just rethink your design. A hashtable might come in handy: it will work and it is cleaner and more efficient.

I'm interpreting this:
What I really want to do, is take this information and build an entire SQL CRUD statement.
...and this:
This is what I really want to do:
...as overriding this:
I want to pass in the type as a string in c, and then get the corresponding SQL type back as a string.
I don't think you should pass the type in as a C string; that gains you nothing, since all you're doing with that string is passing it right back to a macro (and macros can do nothing with strings). If instead you pass the token to the macro, you can have the C preprocessor itself do the lookup.
Here's what I mean:
#define SQL_LOOKUP_TABLE(x) DB_TYPE_FOR_ ## x
#define DB_TYPE_FOR_double "REAL"
#define DB_TYPE_FOR_int "INTEGER"
#define DB_TYPE_FOR_char "TEXT"
Pass the token to this macro, not the stringified token:
#define EXPAND_AS_CREATE_STATEMENT(type, element, struct_name) \
SQL_LOOKUP_TABLE(type) " " # element ", "
...and this:
"CREATE TABLE test_data1 (" test_data1_TABLE(EXPAND_AS_CREATE_STATEMENT) ");"
...just expands to this:
"CREATE TABLE test_data (" "REAL" " " "yomama" ", " "INTEGER" " " "x" ", " "TEXT" " " "shaboopy" ", " ");"
...which after string literal concatenation is equivalent to:
"CREATE TABLE test_data (REAL yomama, INTEGER x, TEXT shaboopy, );"
No strcmp's, no chains of ternaries, no hashtables required. Now, I'm not sure if your database engine is lazy enough to accept trailing commas in the parameter list, but that's another issue (the simplest resolution would be to add delimiter macro support to your X macro).

#Downvoter' s answer explains the reason of the error you get.
I think it's better to use a function in your case.
something like:
char* build_str(char* type)
{
static char str[80];
char* sql_type_str = SQL_LOOKUP_TABLE(type);
strcat(str, "CREATE TABLE (");
strcat(str,sql_type_str);
/* strcat others params */
return str;
}
Maybe you have to give more details on how you plan to use BUILD_STRING.

Related

How to parametrize single string containing # in C preprocessor?

I want to create parameterized preprocessor macro for IBM Metal C Prolog.
The initial unparametrized form is
#pragma prolog(Foo, " #MCPROLG MAIN=(YES,16,132)")
The real prolog is more complex, but for the sake of the question the important part is that there are values within a string.
#pragma directive itself can't be part of a macro, so I replace that with _Pragmalike that:
_Pragma("prolog(Foo, \" #MCPROLG MAIN=(YES,16,132)\")")
I can parameterize Foo like this:
#define STR(...) #__VA_ARGS__
#define PROLOG(function) _Pragma(STR(prolog( function , " #MCPROLG MAIN=(YES,16,132)")))
How can I create macro that parameterizes the value 16?
It appears that I need to concatenate strings within preprocessor, I've tried following approaches. All use this stringization macro:
#define STR(...) #__VA_ARGS__
Token that is replaced with 16 (let's name it size) can't be within a string itself so that it is replaced.
#define PROLOG(function, size) _Pragma(STR(prolog( function , " #MCPROLG MAIN=(YES, size ,132)")))
_Pragma only accepts a single string, so I can't peruse C string concatenation like this:
#define PROLOG(function, size) _Pragma(STR(prolog( function , " #MCPROLG MAIN=(YES," #size ",132)")))
I can't stringize the whole second argument of prolog like this:
#define PROLOG(function, size) _Pragma(STR(prolog( function , STR( #MCPROLG MAIN=(YES, size ,132)))))
because #MCPROLG needs to stay within a string so that its # is not treated as stringization token.
To address issue 1 (need to expand a parameter in the replacement list), you need an indirect stringify macro:
#define STR(X) STR_I(X)
#define STR_I(X) #X
You may make this variadic if you want but it's not necessary here (the commas in your string are surrounded by parentheses; the preprocessor will match those; e.g., FOO(A=(B,C,D)), given FOO is a function-like macro, has one argument).
To address issue 2, yes, you need to stringify the whole thing. Effectively there's no such thing as string literal concatenation to the preprocessor (because it runs in translation phase 4, and string literal concatenation doesn't happen until translation phase 6).
To address issue 3, just produce a hash from another macro (root it at an object-like macro, where # has no special meaning):
#define HASH #
#define HASHM() HASH
The function-like variant allows you to produce the same hash right next to something else (HASHMCPROLG does nothing useful; HASH MCPROLG produces # MCPROLG, HASHM()MCPROLG produces #MCPROLG).
Those pieces in hand, the rest is easy:
#define PROLOG(FN_,SZ_) _Pragma(STR(prolog(FN_, STR( HASHM()MCPROLG MAIN=(YES,SZ_,132)))))
Here I'm assuming you also need an end parentheses around the pragma prolog and that in the question this was a typo; that is, it should be:
_Pragma("prolog(foo, \"#MCPROLG MAIN=(YES,16,132)\")")
...not:
_Pragma("prolog(foo, \"#MCPROLG MAIN=(YES,16,132)\"")

Is there a one-macro way to prefix and quote C macro argument

So when looking into getting my define macro to work, I found the # and ## macro helpers, and used them to simplify my macro. The key part of the macro sets a variable to a string containing the name of the variable (but not the variable name alone). As a simplified example, let's take a macro called SET(X) that should expand SET(something) into something = "pre_something".
The only way I've found to do it so far is with two macros like #define QUOTE(X) #X and #define SET(X) X = QUOTE(pre_##X). However, using multiple macros seems excessive, and may cause problems with further macro expansion (I think). Is there a cleaner, one-line way of doing the same thing?
#define SET(x) x = "pre_"#x
C does string concatenation at compile time, so two string literals next to each other are concatenated.
"hello " "world" -> "hello world"

mixture of string and number in macro

I want to use this macro to put (if 'i' is greater than zero) the symbol '^' and the number I pass (i) to the macro
#define ESP(i) ((i>0) ? ("^"(i)) : "")
I want to call it in this way
printf("%+d%s", n1, ESP(i));
where 'i' is the index of a cycle, but the compilation reports me errors;
how can I modify the code to be right?
Somewhat dirty but should work:
#include <stdio.h>
#define DYNFORMAT(n, i) (i>0) ?"%+d%s%d\n" :"%+d%s%s\n", n, (i>0) ?"^" :"", (i>0) ?i :""
int main(void)
{
int i = 0;
printf(DYNFORMAT(42, i));
i = 1;
printf(DYNFORMAT(42, i));
}
This should print:
+42
+42^1
Disclaimer: I am not sure whether this conforms to the Standard and how to get rid of the warning(s) it gives during compilation.
The clean approach would be to use two calls to printf().
This can be implemented as a macro or a function.
As I love the pre-processor, the macro version here:
#define PRINT_ESP(n, i) \
do { \
if (i = 0) \
printf("%+d", n); \
else \
printf("%+d^%d", n, i); \
} while (0);
Macros operate at compile time, not at run time. They can perform a variety of text-mangling tricks, but they do not evaluate anything. (They can certainly, however, expand to code that evaluates something.) Putting the formatted value of variable i into a string involves evaluating i; no macro can do this.
You could instead expand the scope of the macro to include the whole printf() call:
#define PRINT_ESP(n1, i) do { \
printf(((i > 0) ? "%+d^%d" : "%+d"), n1, i); \
} while (0)
Alternatively, you could use a macro to express just the format selection incorporated into the above macro definition, or you could just put the full printf() call above directly into your code.
All of these variations are based on the fact that arguments in excess of those required by the given format are evaluated prior to the call, but ignored by printf() itself.
You don't need the (i) after the "^". Change your macro to this and it should work #define ESP(i) ((i>0) ? ("^") : ("")). Then in your printf statement if you want to print the value of i after the "^" then have something like this printf("%s%d", ESP(i), i); As far as I know I don't think you can format a string to include an integer inside a macro since that would require calling other functions, so you have to bring the i into the string inside your printf.

Renaming a macro in C

Let's say I have already defined 9 macros from
ABC_1 to ABC_9
If there is another macro XYZ(num) whose objective is to call one of the ABC_{i} based on the value of num, what is a good way to do this? i.e. XYZ(num) should call/return ABC_num.
This is what the concatenation operator ## is for:
#define XYZ(num) ABC_ ## num
Arguments to macros that use concatenation (and are used with the operator) are evaluated differently, however (they aren't evaluated before being used with ##, to allow name-pasting, only in the rescan pass), so if the number is stored in a second macro (or the result of any kind of expansion, rather than a plain literal) you'll need another layer of evaluation:
#define XYZ(num) XYZ_(num)
#define XYZ_(num) ABC_ ## num
In the comments you say that num should be a variable, not a constant. The preprocessor builds compile-time expressions, not dynamic ones, so a macro isn't really going to be very useful here.
If you really wanted XYZ to have a macro definition, you could use something like this:
#define XYZ(num) ((int[]){ \
0, ABC_1, ABC_2, ABC_3, ABC_4, ABC_5, ABC_6, ABC_7, ABC_8, ABC_9 \
}[num])
Assuming ABC_{i} are defined as int values (at any rate they must all be the same type - this applies to any method of dynamically selecting one of them), this selects one with a dynamic num by building a temporary array and selecting from it.
This has no obvious advantages over a completely non-macro solution, though. (Even if you wanted to use macro metaprogramming to generate the list of names, you could still do that in a function or array definition.)
Yes, that's possible, using concatenation. For example:
#define FOO(x, y) BAR ##x(y)
#define BAR1(y) "hello " #y
#define BAR2(y) int y()
#define BAR3(y) return y
FOO(2, main)
{
puts(FOO(1, world));
FOO(3, 0);
}
This becomes:
int main()
{
puts("hello " "world");
return 0;
}

Using symbol name as string literal in #define function macro

I want a debug function-macro that works like this:
int myVar = 5;
PRINTVAR(myVar); // macro
// which expands to something like...
print("myVar: ");
println(myVar);
Basically, I want to use the identifier as a string literal as well as a variable.
I'm just getting a bit sick of repeating myself when I want to dump out a whole lot of variables to the stdout.
My silly attempt, which of course doesn't work:
#define PRINT_VAR(x) Serial.print("x: "); Serial.println(x);
Is this possible?
The "stringizing operator" is designed for precisely this case:
#define PRINT_VAR(x) (print(#x ": "), println(x))
Look up the stringifying operator, #, when you use the macro id prefixed with this, it puts it as a string instead of expanding it.
Giving your code example, I don't know if you're talking about C or Java. However, here is I'll do in C :
#define DEBUG(X, ...) fprintf(x, __VA_ARGS__);
And to use it :
DEBUG(srderr, "my error\n");

Resources