I am currently developing a unit test library in C and I wanted to add a functionality Catch (in C++) had.
When you do, using Catch :
int x = 5;
REQUIRE(x == 4);
The output will be something like this :
Failed statement : x == 4
evaluated as 5 == 4
Is it possible to do something like that in C ?
This needs to be done at runtime.
You could do something like this. It's not very convenient, but it works:
#include <stdio.h>
#include <stdlib.h>
#define ASSERT_INT 1
#define ASSERT_EQ(x,y,type) do { if((x)!=(y)) { \
switch(type) { \
case ASSERT_INT: \
printf("Failed statement : %s == %d\n\tevaluated as %d == %d\n", \
#x, (y),(x), (y)); break; \
} exit(EXIT_FAILURE); }} while(0)
It requires some work though. If you want many operations you can do like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ASSERT_INT 1
#define ASSERT_EQ 1
#define ASSERT(x,y,op,type) do { \
int success; \
char op_str[3]; \
switch(op) { \
case ASSERT_EQ: \
strcpy(op_str, "==");success = ((x) == (y)); break; \
} \
if(!success) { \
switch(type) { \
case ASSERT_INT: \
printf("Failed statement : %s %s %d\n\tevaluated as %d %s %d\n", \
#x, op_str, (y),(x), op_str, (y)); break; \
} exit(EXIT_FAILURE); }} while(0)
And the output:
$ ./a.out
Failed statement : x == 4
evaluated as 5 == 4
You will need to add a modified version of strcpy(op_str, "==");success = ((x) == (y)); break; for each different logical operation and a modified version of printf("Failed statement : %s %s %d\n\tevaluated as %d %s %d\n", #x, op_str, (y),(x), op_str, (y)); break; for each different type.
Related
I have a project with many files and they all include one header called logging.h, I dont know what happened but for some reason when i try to compile this happens. I can not revert the changes and I dont know what to do.
These lines repeat themselves multiple times
/usr/bin/ld: fs/operations.o: in function `send_request':
/root/so/mbroker/utils/logging.h:59: multiple definition of `send_request'; mbroker/mbroker.o:/root/so/mbroker/utils/logging.h:59: first defined here
/usr/bin/ld: fs/operations.o: in function `serialize':
/root/so/mbroker/utils/logging.h:68: multiple definition of `serialize'; mbroker/mbroker.o:/root/so/mbroker/utils/logging.h:68: first defined here
/usr/bin/ld: fs/state.o: in function `clear_session':
/root/so/mbroker/utils/logging.h:53: multiple definition of `clear_session'; mbroker/mbroker.o:/root/so/mbroker/utils/logging.h:53: first defined here
As you can see it's all happening in logging.h
Here's my logging.h
#ifndef __UTILS_LOGGING_H__
#define __UTILS_LOGGING_H__
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h> //open
#include <fcntl.h> //flags for open
#include <unistd.h>
#define PIPENAME 256
#define BOXNAME 32
#define MSIZE 256
#define COMMAND 8
static const __uint8_t REGISTER_PUBLISHER = 1;
static const __uint8_t REGISTER_SUBSCRIBER = 2;
static const __uint8_t CREATE_BOX = 3;
static const __uint8_t REMOVE_BOX = 5;
static const __uint8_t LIST_BOX = 7;
static const char INVALID_SESSIONS[] = "Invalid number of sessions";
static const char PIPENAME_TOO_BIG[] = "The pipe's name has too many characters";
static const char INVALID_BOXNAME[] = "The box's name is invalid";
static const char INVALID_NUMBER_OF_ARGUMENTS[] = "Insufficient or too many arguments";
static const char INVALID_ARGUMENTS[] = "One of the arguments is invalid";
static const char UNEXISTENT_PIPE[] = "Pipe doesn't exist";
static const char EXISTENT_PIPE[] = "Pipe already exists";
static const char ERROR_WRITING_PIPE[] = "Error writing to pipe";
static const char SIGNAL_FAIL[] ="Failed to initialize signals";
typedef struct box{
char boxname[BOXNAME];
int hasWriter;
int n_readers;
struct box *next;
} box;
typedef struct req{
int _code;
char _client_pipe[PIPENAME];
char _box_name[BOXNAME];
} request;
typedef enum {
LOG_QUIET = 0,
LOG_NORMAL = 1,
LOG_VERBOSE = 2,
} log_level_t;
//deletes fifo
void clear_session(int fd, char* fn){
close(fd);
unlink(fn);
}
/*writes to pipe tx a pointer with information*/
void send_request(int tx, request *r1) {
ssize_t ret = write(tx, &r1, sizeof(r1));
if (ret < 0) {
fprintf(stdout, "ERROR: %s\n", ERROR_WRITING_PIPE);
exit(EXIT_FAILURE);
}
}
/*Returns a pointer to a struct containing the request*/
request *serialize(int code, char* client_pipe, char* box_name){
request *r1 = (request*) malloc(sizeof(request));
r1->_code = code;
strcpy(r1->_client_pipe, client_pipe);
strcpy(r1->_box_name, box_name);
return r1;
}
void sig_handler(int sig);
void set_log_level(log_level_t level);
extern log_level_t g_level;
#define INFO(...) \
do { \
char buf[2048]; \
snprintf(buf, 2048, __VA_ARGS__); \
fprintf(stderr, "[INFO]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, \
__func__, buf); \
} while (0);
#define PANIC(...) \
do { \
char buf[2048]; \
snprintf(buf, 2048, __VA_ARGS__); \
fprintf(stderr, "[PANIC]: %s:%d :: %s :: %s\n", __FILE__, __LINE__, \
__func__, buf); \
exit(EXIT_FAILURE); \
} while (0);
#define WARN(...) \
do { \
if (g_level == LOG_NORMAL || g_level == LOG_VERBOSE) { \
char buf[2048]; \
snprintf(buf, 2048, __VA_ARGS__); \
fprintf(stderr, "[WARN]: %s:%d :: %s :: %s\n", __FILE__, \
__LINE__, __func__, buf); \
} \
} while (0);
#define LOG(...) \
do { \
if (g_level == LOG_NORMAL || g_level == LOG_VERBOSE) { \
char buf[2048]; \
snprintf(buf, 2048, __VA_ARGS__); \
fprintf(stderr, "[LOG]: %s:%d :: %s :: %s\n", __FILE__, \
__LINE__, __func__, buf); \
} \
} while (0);
#define DEBUG(...) \
do { \
if (g_level == LOG_VERBOSE) { \
char buf[2048]; \
snprintf(buf, 2048, __VA_ARGS__); \
fprintf(stderr, "[DEBUG]: %s:%d :: %s :: %s\n", __FILE__, \
__LINE__, __func__, buf); \
} \
} while (0);
#endif // __UTILS_LOGGING_H__
I tried deleting files, going back as far as i could but this error wont stop haunting me.
I also tried make clean multiple times, but did nothing either.
I went back to an old backup of the project which was ok and now it's showing me this same error message again. I tried compiling both through vsc and through wsl.
Now that i think it through, the error might have started when i included .c in another file. I have long deleted that include line but am still getting the errors.
The solution involved only writing prototypes on the header and implementing the functions in a logging.c. It's weird the compiler only warned me after like many and many trials, but it's solved and that's all i could ask for.
#include<stdio.h>
#include<errno.h>
#include<error.h>
#include<stdlib.h>
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define DEBUG_ERROR(M,...) fprintf(stderr, "[ERROR] (%s:%d: (error: %s)) M \n", __FILE__, __LINE__, clean_errno(),##__VA_ARGS__)
int main()
{
int test =10;
DEBUG_ERROR("Test variable %s = %d.\n","test",test);
return 0;
}
Any ideas on how to fix the following warning with the debug macro...
warn.c:12:4: warning: too many arguments for format [-Wformat-extra-args]
Your macro should be:
#define DEBUG_ERROR(M,...) fprintf(stderr, "[ERROR] (%s:%d: (error: %s)) " M " \n", __FILE__, __LINE__, clean_errno(),##__VA_ARGS__)
Note that M is not in quotes now.
I'm going through Zed Shaw's tutorial on C debug macros, and am running an undeclared label problem, call the following file debug_macro.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
int test_check(char *file_name)
{
FILE *input = NULL;
char *block = NULL;
block = malloc(100);
input = fopen(file_name, "r");
check(input, "Failed to open %s.", file_name);
free(block);
fclose(input);
return 0;
error:
if(input) free(input);
if(block) free(block);
return -1;
}
int main(int argc, char *argv[])
{
// open up a bogus file and then trigger error
check(test_check("bogus.txt") == 0, "failed with bogus.txt");
return 0;
}
When I compile it with either cc or gcc, I get the following error:
gcc -Wall -g -O0 -I/opt/X11/include goto.c -o goto
goto.c:36:5: error: use of undeclared label 'error'
check(test_check("bogus.txt") == 0, "failed with bogus.txt");
^
goto.c:10:78: note: expanded from macro 'check'
#define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
^
1 error generated.
make: *** [goto] Error 1
shell returned 2
Press ENTER or type command to continue
When the macro is expanded, it inserts a goto error inside the test_check function, which has an error: label defined, so I don't know why I'm getting this compiler error.
You are trying to jump from main to another function test_check, but goto can only jump to a label inside the same function.
C11 ยง6.8.6.1 The goto statement
A goto statement causes an unconditional jump to the statement prefixed by the named
label in the enclosing function.
Is there a way to make the log-level checking in my LOGGING-macro (shown below) to happen during compile-time? It should be possible as it is already known during compile time whether the condition (if (pLogLevel <= LOG_LEVEL)) is true or false. Here is an example of how the macro could be used:
LOGGING(LOG_DEBUG, "FALSE - Wrong Marker\n");
Originally I thought that #ifdirectives within a #define could help me, but this article showed me that this would not be possible. This is the source code to my problem:
#ifndef __LOGGING_H__
#define __LOGGING_H__ 1
#include <time.h>
#define LOG_EMERG 0
#define LOG_ALERT 1
#define LOG_CRIT 2
#define LOG_ERROR 3
#define LOG_WARN 4
#define LOG_NOTICE 5
#define LOG_INFO 6
#define LOG_DEBUG 7
#define LOG_NONE 8
/* set the global logging level here */
#define LOG_LEVEL LOG_INFO
void print_timestamp(void);
#if LOG_LEVEL == LOG_DEBUG
#define _LOG_PREAMBLE \
fprintf(stdout, "%s:%d ", __FILE__, __LINE__);
#else
#define _LOG_PREAMBLE \
print_timestamp();
#endif
#if LOG_LEVEL == LOG_EMERG
#define _LOG_LEVEL fprintf(stdout, "EMERG ");
#elif LOG_LEVEL == LOG_ALERT
#define _LOG_LEVEL fprintf(stdout, "ALERT ");
#elif LOG_LEVEL == LOG_CRIT
#define _LOG_LEVEL fprintf(stdout, "CRIT ");
#elif LOG_LEVEL == LOG_ERROR
#define _LOG_LEVEL fprintf(stdout, "ERROR ");
#elif LOG_LEVEL == LOG_WARN
#define _LOG_LEVEL fprintf(stdout, "WARN ");
#elif LOG_LEVEL == LOG_NOTICE
#define _LOG_LEVEL fprintf(stdout, "NOTICE ");
#elif LOG_LEVEL == LOG_INFO
#define _LOG_LEVEL fprintf(stdout, "INFO ");
#elif LOG_LEVEL == LOG_INFO
#define _LOG_LEVEL fprintf(stdout, "DEBUG ");
#else
#define _LOG_LEVEL
#endif
#define LOGGING(pLogLevel, ...) \
/* change this to compile time conditional if possible */ \
if (pLogLevel <= LOG_LEVEL) \
{ \
_LOG_PREAMBLE \
_LOG_LEVEL \
fprintf(stdout, ##__VA_ARGS__); \
}
#endif /* __LOGGING_H__ */
One approach would be to combine the log level into the logging macro.
This would create a set of macros that would be turned on or off depending on the log level.
You would use these macros and they would appear or not depending on the log level at the time of the compile. So they would be used like the following.
LOGGING_LEVEL_DEBUG("a Debug log.");
//.... some code
LOGGING_LEVEL_EMERG("a Emerge log.");
So the macros definitions would look something like the following:
#define LOG_EMERG 0
#define LOG_ALERT 1
#define LOG_CRIT 2
#define LOG_ERROR 3
#define LOG_WARN 4
#define LOG_NOTICE 5
#define LOG_INFO 6
#define LOG_DEBUG 7
#define LOG_NONE 8
/* set the global logging level here */
#define LOG_LEVEL LOG_INFO
void print_timestamp(void);
#if LOG_LEVEL >= LOG_DEBUG
#define _LOG_PREAMBLE \
fprintf(stdout, "%s:%d ", __FILE__, __LINE__);
#else
#define _LOG_PREAMBLE \
print_timestamp();
#endif
#if LOG_LEVEL >= LOG_EMERG
#define LOGGING_LEVEL_EMERG(...) \
{ \
_LOG_PREAMBLE \
fprintf(stdout, "EMERG "); \
fprintf(stdout, ##__VA_ARGS__); \
}
#else
#define LOGGING_LEVEL_EMERG(...)
#endif
#if LOG_LEVEL >= LOG_ALERT
#define LOGGING_LEVEL_ALERT(...) \
{ \
_LOG_PREAMBLE \
fprintf(stdout, "ALERT "); \
fprintf(stdout, ##__VA_ARGS__); \
}
#else
#define LOGGING_LEVEL_ALERT(...)
#endif
You could define the macros themselves conditionally:
#if LOG_LEVEL > 0
# define LOG(...) printf(__VA_ARGS__)
#else
# define LOG(...)
#endif
#if LOG_LEVEL > 1
// and so forth
I've got the following code:
#define checkLiteMessage \
{ \
#ifdef LITE_VERSION \
if (alertView.tag == 100) \
{ \
if (buttonIndex == 1) \
[ [UIApplication sharedApplication] openURL: buyAppLink]; \
[alertView release]; \
return; \
} \
#endif \
} \
What I want to do is to have the following code to be included every time I call checkLiteMessage:
#ifdef LITE_VERSION
if (alertView.tag == 100)
{
if (buttonIndex == 1)
[ [UIApplication sharedApplication] openURL: buyAppLink];
[alertView release];
return;
}
#endif
What's my problem? Why doesn't this code compile?
Thanks.
You've specified the macro with line continuations, which is correct. However, that means the #ifdef statement is not at the start of the line, so the preprocessor isn't going to do it.
You can't have the #ifdef embedded inside the macro. You could reverse that:
#ifdef LITE_VERSION
#define checkLiteMessage do { \
if (alertView.tag == 100) \
{ \
if (buttonIndex == 1) \
[ [UIApplication sharedApplication] openURL: buyAppLink]; \
[alertView release]; \
return; \
} \
} while(0)
#else
#define checkLiteMessage do { ; } while (0)
#endif
I'll add that putting a 'return' statement inside a macro is pretty evil and will be confusing to everyone. Don't do it.
The problem is that you cannot include preprocessor directives in a macro.
You may want to do that instead :
#ifdef LITE_VERSION
#define checkLiteMessage \
{ \
if (alertView.tag == 100) \
{ \
if (buttonIndex == 1) \
[ [UIApplication sharedApplication] openURL: buyAppLink]; \
[alertView release]; \
return; \
} \
}
#else
#define checkLiteMessage
#endif
Also make sure that the "return" line does what you think it does (here, it exits the function calling checkLiteMessage, not just the macro (a macro cannot be "exited").
The the backslash-linefeed combination is equivalent to no letters at all so
#define x y \
z
is equivalent to
#define x y z
Also, according to the standard, preprocessor directives in a macro body are not expanded or interpreted and that #ifdef is passed to the compiler instead of conditionally dropping the code in between.
You may inspect this behaviour creating a file a.c with the contents
#define x y \
z
#define v \
#ifdef E
x
v
and preprocessing it with the command gcc -E a.c.
In your situation, what you need to do is
#ifdef LITE_VERSION
#define checkLiteMessage \
{ \
if (alertView.tag == 100) \
{ \
if (buttonIndex == 1) \
[ [UIApplication sharedApplication] openURL: buyAppLink]; \
[alertView release]; \
return; \
} \
}
#else
#define checkLiteMessage
#endif
The preprocessor is one-pass only. So the sequence #ifdef is getting to your compiler, causing an error. Make it a function.