Galactasm
Galactasm

Reputation: 97

Macro Expansion Ternary Lookup Table

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.

Upvotes: 0

Views: 906

Answers (3)

H Walters
H Walters

Reputation: 2654

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).

Upvotes: 1

Mouin
Mouin

Reputation: 1103

@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.

Upvotes: 0

cadaniluk
cadaniluk

Reputation: 15229

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.

Upvotes: 2

Related Questions