C Preprocessor, strip parens from string - c-preprocessor

I'm working with some C preprocessor macros that, thanks to changes elsewhere, are now showing up like (("Foo")) instead of "Foo". The problem is, elsewhere in the code, this is breaking concatenations.
E.G.
#include "some_external_FOO_and_FILE_PATH_declarations.h"
//FOO is (("Foo"))
const char* filepath = FILE_PATH(FOO "/bar")
//throws error
How can I get FOO to look like "Foo" again?

This ended up working for me:
#define UNPAREN(...) #__VA_ARGS__
const char *filepath = FILEPATH(UNPAREN(FOO) "bar")

Related

What does #define __UNUSED__ do?

I am going through a C code and I found something like this:
#define __UNUSED__
char buf[MAX_BUF_LENGHT];
int errors=0;
What does this mean?
I am not aware that __UNUSED__ is a predefined preprocessor symbol. So it must be a user defined symbol.
I myself have sometimes (test) code or obsolete code in a c-file that I mark-out with #ifdef BLIEP (and BLIEP is normally not defined), but can put it back into compilation by placing a #define BLIEP. Probably the original author of this code did something similar with __UNUSED__.

xc8 warning: initial value for var differs

I've been running into a compiler warning:
version.h:47: warning: (1478) initial value for "_svn_string_revision" differs to that in version.h:47
the corresponding version.h file looks like this:
#ifndef _VERSION_H_
#define _VERSION_H_
#define SVN_REVISION_NUMBER 31
const char *svn_string_revision = "31"; // line 47
#endif //_VERSION_H_
Usage:
main.c:
#include "version.h"
// I do not use svn_string_revision here.
// I only use SVN_REVISION_NUMBER
#pragma config IDLOC3=SVN_REVISION_NUMBER
otherfile.c:
#include "version.h"
// still no usage of svn_string_revision, only this:
EUSART_Write(SVN_REVISION_NUMBER);
So far this is descriptive and clear. I assume the problem is that the const char string is defined in a header file, which gets included in more than one source code file. So the compiler sees more than one "svn_string_revision" variable and treats it as redeclaration. But normally the value should be always the same. My version.h file is an auto generated file which gets regenerated prior to every build.
Has somebody encountered this before, and how can I handle that?
The clean approach would be to use a version.h file complemented with a version.c, where the header declares
extern const char *svn_string_revision;
and the source
const char *svn_string_revision = "31";
But this would require me to rewrite the automated code generation, which I would like to avoid.
Long story short, my questions are:
Is my understanding of the warning correct?
How can I avoid those warnings gracefully, given that I don't want to split up version.h into a .c and .h file
First solution :
static const char *svn_string_revision = "31";
The static will make the variable local to each C file, so no conflict can occur. Since it is a read only constant it should be fine. However, this means there will be many copies of the variable in the program. A good compiler can optimize this, but in my experience, I'm not sure XC8 will do that.
Second solution, probably better :
#define SVN_REVISION_NUMBER 31
#define STRINGIFY(s) #s
extern const char *svn_string_revision;
// in version.c
const char *svn_string_revision = STRINGIFY(SVN_REVISION_NUMBER);
Or just :
#define SVN_REVISION_NUMBER 31
#define VERSION_STRING "31"
extern const char *svn_string_revision;
// in version.c
const char *svn_string_revision = VERSION_STRING;
You could also just remove svn_string_revision and use VERSION_STRING instead, but you should check before that XC8 doesn't create many copies of the string.

How can I share a const char array between two source files gracefully?

To simplify my code, I make the code snippet below to explain my question:
def.h
#ifndef _DEF_H_
#define _DEF_H_
const char draw[] = "Draw on the canvas:"
#endif
circle.c
#include "def.h"
void draw_circle(void)
{
printf("%s %s", draw, "a circle.");
}
main.c
#include "def.h"
int main(void)
{
printf("%s %s", draw, "nothing.");
}
The problem is that there will be no problem at compile-time but it will probably fail on link-time because of the redefinition of const char array, draw[].
How to prevent this problem to share a const char array between two source files gracefully without putting them into a single compilation unit by adding #include"circle.c" at the top of main.c?
Is it possible?
You get multiple definition error when linking because, you put the definition of draw in a header file. Don't do that. Put only declarations in the header, and put definitions in one .c file.
In you example, put this in a .c file
const char draw[] = "Draw on the canvas:"
And put this in the header:
extern const char draw[];
Unfortunately there are no real named constants in C for such types. The only possibility are macros. For strings you could do
#define draw "Draw on the canvas:"
or if you want to insist that it is const qualified:
#define draw (char const*)"Draw on the canvas:"
#define draw (char const[]){ "Draw on the canvas:" }
The last shows the possibility for other types, use compound literals. Here using const is important, such that the compiler may optimize better.

Cgo linker errors on C constants

I'm using cgo to wrap a C library and have run into a strange set of linker errors. I've boiled the problem down to the following:
A file header.h contains
#ifndef HEADER_H
#define HEADER_H
#define CONSTANT1 ("")
#define CONSTANT2 ""
#define CONSTANT3 ((char*)0)
#define CONSTANT4 (char*)0
#endif /* HEADER_H */
And test.go contains
package main
/*
#include "header.h"
*/
import "C"
func main() {
_ = C.CONSTANT1
_ = C.CONSTANT2
_ = C.CONSTANT3
_ = C.CONSTANT4
}
Upon running go run test.go I get the following error:
# command-line-arguments
... _cgo_main.o:(.data.rel+0x0): undefined reference to `CONSTANT4'
... _cgo_main.o:(.data.rel+0x8): undefined reference to `CONSTANT3'
... _cgo_main.o:(.data.rel+0x10): undefined reference to `CONSTANT1'
collect2: ld returned 1 exit status
I have two questions about this:
Why does the linker have anything to do with pre-defined constants?
Why do CONSTANT1, CONSTANT3, CONSTANT4 show up as undefined, but not CONSTANT2?
Thanks in advance.
*Edit: Constants defined as other values (e.g. ints) work fine.
*Edit2: Using go version go1.1.2 linux/amd64
*Edit3: A complete example of failure:
I'm working with the C OpenLDAP library and would like to use the LDAP_SASL_SIMPLE constant. It's defined in ldap.h as
#define LDAP_SASL_SIMPLE ((char*)0)
#define LDAP_SASL_NULL ("")
The LDAP_SASL_NULL constant gives the same error.
A minimal demonstrative go program:
package main
/*
#cgo LDFLAGS: -lldap
#include <ldap.h>
*/
import "C"
func main() {
_ = C.LDAP_SASL_SIMPLE
}
I did originally answer something different based on how I thought cgo works. But if CONSTANT2 is recognized by cgo, something different might be the reason. Could you please:
run the tool nm on the library file and tell whether the output contains CONSTANT2 or any other constants you refer to that were at the same time established through #define. A library might declare a #define constant at the same time as a global symbol to get around compatibility issues.
provide a minimal working example of your problem if possible. This is, an example that can be compiled by someone who reads your post and exhibits your problem. Your question looks like it may miss some important parts to answer it. For instance, it would be nice to know the actual library you have problems with.
original answer
If you use #define, you do not establish anything the compiler will actually see. A #define is a preprocessing directive that is removed before parsing. To establish constants the compiler (and thus cgo) can see, actually declare them:
const char *CONSTANT1 = "";
const char *CONSTANT2 = "";
const char *CONSTANT3 = (char*)0;
const char *CONSTANT4 = (char*)0;
If you cannot touch the header, there is typically not much you can do; you basically have to duplicate all constants in the Go part of your code:
const (
CONSTANT1 = "",
CONSTANT2 = "",
CONSTANT3 = nil,
CONSTANT4 = nil,
)
You could try complex tricks like running cpp over your Go code, but this is likely to cause more trouble than it would solve problems.

C-Macro in if-Condition

quite a simple problem I have here.
I have a little Macro for a global Variable which is defined in my Header like this:
extern bool uart_message_received;
#define get_uart_message_rec() uart_message_received;
In my C-File I want to access the file like this:
bool uart_message_received = 0;
void foo(void)
{
bool test;
test = get_uart_message_rec(); // Works fine
if(get_uart_message_rec()==0) // Doesn't work
{
//...
}
}
I am a little confused why the condition in the if is not working. Am I doing something wrong, or am I violating some C directives?
#define get_uart_message_rec() uart_message_received
// ^ no semicolon
Macro replacement will substitute the text as is, including the ; in your case. Which will lead to syntax errors in the if case.
Remove the colon at the end of :
#define get_uart_message_rec() uart_message_received;
Because it becomes:
if(**uart_message_received;**==0) // Doesn't work
{
//...
}
When the preprocessor basically does find/replace on your code.
You have a semicolon on the end of your macro - remove that, and it will be fine. Note that macros do replace exactly as written, so your macro expands from:
if(get_uart_message_rec()==0)
to:
if(uart_message_received; ==0)
which should make the compiler error.
[writing too slowly!]
It's because you have a semicolon at the end of the macro.
Macros are replaced as is, before the actual compiler sees the text, so the statement after replacement looks like this:
if(uart_message_received;==0)

Resources