Check is member in struct with C - c

I want to access to member in struct but check if this member exist
#include <stdio.h>
int main()
{
struct MyStruct { int a;};
struct MyStruct temp ;
temp.a=3;
return 0;
}
Is there a way to check if member a is in struct m and if not access to b with #ifdef ?
something like #ifdef MyStruct.a temp.a=3; #else temp.b=3; #endif

It's not possible to do in C.
The usual way if you really want to detect a member of a struct, is to first compile a sample program that uses the struct, see if the compilation succeeds and depending on the result define or not a preprocessor macro to the compiler used to compile the rest of the project. Various build systems were created to ease up such tasks. A short example with cmake may look like the following:
cat >mystruct.h <<EOF
struct MyStruct {
int a; // a? maybe not? no one knows.
};
EOF
cat >CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.11)
project(test_include C)
# will try to compile a testing file
try_compile(mystruct_has_a "${CMAKE_BINARY_DIR}/temp" ${CMAKE_SOURCE_DIR}/test_mystruct_has_a.c)
add_executable(main main.c)
if(mystruct_has_a)
target_compile_definitions(main PUBLIC MYSTRUCT_HAS_A)
endif()
EOF
cat >test_mystruct_has_a.c <<EOF
#include "mystruct.h"
int main() {
struct MyStruct temp;
temp.a = 3; // test - can we compile this?
}
EOF
cat >main.c <<EOF
#include <stdio.h>
#include "mystruct.h"
int main() {
struct MyStruct temp ;
#if MYSTRUCT_HAS_A
// it's ok to use `temp.a` here.
temp.a = 3;
printf("%d\n", temp.a);
#endif
}
EOF
Which can be compiled with from the command line:
cmake -S. -B_build && cmake --build _build --verbose
cmake will try to compile file test_mystruct_has_a.c. If the compilation is successful, then the macro MYSTRUCT_HAS_A will be added as a macro to compiler arguments. If not, the macro will not be added. Then main.c will compile with that macro or without depending on the previous result. Such a way is typically used in many, many various projects mostly to provide portability by detecting operating system specific stuff, from top of my head for example members of sigaction, siginfo_t or sched_param.

Related

Header and Source file problems in Code::Blocks

I am programming a game in C using Code::Blocks. I am using the most recent version of C and of Code::Blocks. I am still learning the language.
All of my past programs have been contained in one huge source file, so I decided to branch out and try putting my code in multiple files. I know that the proper way to do it is to have source files containing code definitions and such and a header file containing prototypes for other source files to use. This has worked out horribly for me and I either can't get the files to work together properly or it simply doesn't work period.
I have a single function in a source file called process.c and a function prototype in a file called process.h. I also have a main.h and a main.c containing all the rest of the code. The main issue is that I have a typedef struct Game in my main.h file and I can't get the 'Game' struct type I created to work in my process.c. Every function in my game needs the Game type in order to work. However, when I give process.c access to main.h (the file that Game is declared in) I get issues.
My code works fine when it's in one file. My header files are protected from duplication and are properly included in the program. The problem is, I need to include main.h in both main.c and process.c. And I have to include process.h in both 'main.c' and 'process.c'. I have tried every configuration and nothing works.
In some #include configurations I get no errors, but I get this weird message that says "It seems your project has not been built yet; would you like to build it now?" and when I click "Yes" nothing happens.
My compiler works fine and there is nothing wrong with the projects settings. What the heck is going on here? How do I get main.h and process.h to work together?
EDIT: Source code:
main.c:
#include <stdio.h>
#include "main.h"
#include "process.h"
void initGame(Game *thisGame)
{
variable = 10;
number = 5;
letter = 'c';
}
int main()
{
Game thisGame;
initGame(&thisGame);
displayData(&thisGame);
return 0;
}
main.h:
#ifndef _MAIN_H_
#define _MAIN_H_
typedef struct
{
int variable, number;
char letter;
}
#endif
process.c:
#include <stdio.h> //not sure if this should be here or not, it doesn't seem to effect my code
#include "main.h"
#include "process.h"
void displayData(Game *thisGame)
{
printf("%i, %i, %c", thisGame.variable, thisGame.number, thisGame.letter);
}
process.h:
#ifndef _MAIN_H_
#define _MAIN_H_
void displayData(Game *thisGame);
#endif
Error message
-------------- Build: Debug in FishKiller (compiler: GNU GCC Compiler)---------------
mingw32-g++.exe -L..\deps\lib -L..\SDLFILES\lib -o bin\Debug\FishKiller.exe obj\Debug\main.o obj\Debug\process.o -lmingw32 -lSDL2main -lSDL2 -lSDL2_image
obj\Debug\process.o:process.c:(.rdata+0x0): multiple definition of `SCREEN_WIDTH'
obj\Debug\main.o:main.c:(.rdata+0x0): first defined here
obj\Debug\process.o:process.c:(.rdata+0x4): multiple definition of `SCREEN_HEIGHT'
obj\Debug\main.o:main.c:(.rdata+0x4): first defined here
obj\Debug\process.o:process.c:(.rdata+0x8): multiple definition of `GAMESTATE_MENU'
obj\Debug\main.o:main.c:(.rdata+0x8): first defined here
obj\Debug\process.o:process.c:(.rdata+0xc): multiple definition of `GAMESTATE_GAME'
obj\Debug\main.o:main.c:(.rdata+0xc): first defined here
obj\Debug\process.o:process.c:(.rdata+0x10): multiple definition of `GAMESTATE_GAMEOVER'
obj\Debug\main.o:main.c:(.rdata+0x10): first defined here
collect2.exe: error: ld returned 1 exit status
Process terminated with status 1 (0 minute(s), 0 second(s))
0 error(s), 0 warning(s) (0 minute(s), 0 second(s))
Issues are address file by file below. Once these issues are corrected in your source, the executable will build.
1)
In process.h you have the wrong header blocks:
#ifndef _MAIN_H_
#define _MAIN_H_
//Change to:
#ifndef _PROCESS_H_
#define _PROCESS_H_
Also change:
void displayData(Game *thisGame);(see notes in main.h for explanation)
To:
void displayData(GAME *thisGame);
2) In process.c you have;
#include "main.h"
It does not hurt anything, but since we are analyzing everything, it is not needed to support current architecture.
You also have:
printf("%i, %i, %c", thisGame.variable, thisGame.number, thisGame.letter);
Because the thisGame is passed in as a pointer, you need to use struct pointer operator: ->
printf("%i, %i, %c", thisGame->variable, thisGame->number, thisGame->letter);
Also, the function protocol in the same file is incorrect. You are attempting to instantiate a variable type that does not exist: (see notes for main.h)
Change:
void displayData(Game *thisGame){...}
To:
void displayData(GAME *thisGame){...}//uses typedef struct GAME
3) in main.h you have a malformed struct:
typedef struct
{
int variable, number;
char letter;
}//no ";" statement terminator to indicate to your compiler _end of struct_
And with this definition, there is no struct name with which to reference or instantiate it in any other file. Change it to the following:
typedef struct
{
int variable;
int number;//style point , put each member on its own line
char letter;
}GAME;//note struct type name and terminator ";"
With the struct type name, in this case GAME, you can create instances of this struct in any file that #includes this file.
extern GAME Game;// using the extern modifier, create an instance of GAME
// that can be referenced in any file of the
//project, once initialized. (See the line GAME Game; in main.c)
4) in main.c you have variables in the function initGame that need to be referenced differently. Change this:
void initGame(Game *thisGame)
{
variable = 10;
number = 5;
letter = 'c';
}
To:
void initGame(GAME *thisGame)
{
thisGame->variable = 10;
thisGame->number = 5;
thisGame->letter = 'c';
}
There is Code::Blocks information here that may help you to set up your environment to get the error messages that will help you to debug your code.

Compiling multiple C files having underlying functions from a common file

I went through all StackOverflow threads regarding this but could not find one that matched my problem.
I am trying to implement different data structures (implemented in stack.c, queue.c) which get basic functions from another file storage.c. Here are their header files -
storage.h
typedef struct _circular_list
{
int *array, head, tail;
size_t size, count;
} circular_list;
typedef circular_list *clist;
clist Initialize(size_t);
clist WriteAtTail(clist, int);
int RemoveAtHead(clist);
int RemoveAtTail(clist);
clist WriteAtHead(clist, int)
stack.h
#include "storage.h"
#define stack clist
#define push(s, i) WriteAtTail(s, i)
int pop(stack);
int popKey(stack, int);
queue.h
#define queue clist
#define EnQueue(q,i) WriteAtTail(q,i)
#define DeQueue(q) RemoveAtHead(q)
I include them in the main C file
driver.c
#include <stdio.h>
#include "stack.h"
#include "queue.h"
Now if i try to compile them using
gcc -o driver driver.c storage.c stack.c queue.c
I get an error unknown type name 'clist' in queue.h.
If I try to include storage.h in queue.h as well, I get an error that I have multiple declarations for the structure.
How do I go about compiling this?
If you don't include storage.h in stack.h or queue.h you have to include it in your driver.c file before including one of the other files. Otherwise the compiler does not know about the declarations of storage.h.
A better solution is to include storage.h directly in stack.h and queue.h. To avoid getting compiler errors due to multiple declarations you have to change your storage.h like this:
#if !defined(INC_STORAGE_H)
#define INC_STORAGE_H
/* original storage.h contents go here */
#endif /* INC_STORAGE_H */
Or simply write #pragma once at the beginning of the storage.h if your compiler supports it. (If you're writing a library for other people to use I recommend the first solution)

Why does my C project compilation cannot find my structure?

I had a project based on a single C file that I try to rearrange for further development in several .c and .h files.
My main is organised as follow:
// General includes
typedef struct
{
} MyStruct;
#include "MyInclude.h"
// Rest of the code
My file "MyInclude.c" is organised as follow:
#include "MyInclude.h"
// Defines
// Functions that need to know MyStruct
There is something I don't understant about the compilation process of GCC. In fact, I got the error "MyStruct undeclared (first use in this function)" and I don't why as I put my include after the typedef declaration of my structure.
Does someone knows why it happens?
The question is a bit unclear.
The file "MyInclude.c" can access only to your H file.
While your struct is written in another C file.
You can solve it by:
Define the struct on the H file "MyInclude.h". It will work, but methodologically it's wrong.
Define setters and getters to access your struct
Cheers
Your file.h :
// file.h
#include <stdio.h> //Juste for printf
typedef struct s_data
{
char c;
} t_data;
Your file.c :
#include "file.h"
int main()
{
t_data data;
data.c = 'a';
printf("%c", data.c);
return (0);
}
Compil (if your file .c and .h are in the same directory):
gcc file.c -o my_app -I .

External Structs and Multiple Headers

I have a confusion about external structs. I have to define a global instance of the struct in a file other than which the struct is defined (as a RAM variable which I do not know what it is exactly).
Below is an example that GCC can compile and it runs correct while Code Composer Studio gives compile-time errors.
I want to learn where the problem is, how GCC can compile, and where/how I should use the extern declaration.
Any comment would be appreciated.
person.h
#ifndef PERSON_H
#define PERSON_H
struct person {
int age;
};
typedef struct person PERSON;
void modifyPerson();
#endif // PERSON_H
personRam.h
#ifndef PERSONRAM_H
#define PERSONRAM_H
#include "person.h"
PERSON p1;
#endif // PERSONRAM_H
modifyPerson.c
#include "person.h"
#include "personRam.h"
void modifyPerson() {
p1.age = 10;
}
main.c
#include <stdio.h>
#include "person.h"
#include "personRam.h"
int main() {
modifyPerson();
printf("%d", p1.age);
return 0;
}
You should not get a compiler error, but a linker error, saying that p1 is defined multiple times. At least that's what I guess is the problem.
The reason is that you define the variable in a header file, which you then include in multiple source files. This means that the definition is in both source files (The preprocessor #include directive literally puts the contents of the header file in place of the include "statement").
If you declare the variable as extern in the header file, and define it in one source file it should work.
So in personRam.h
extern PERSON p1;
And in one of the source files:
PERSON p1;
On which operating system are you compiling, and for which target system?
For what it is worth, Linux (& Unix-es) and Windows have different linking semantics.
Read Levine's "Linkers & Loaders" book for details.

ctags chokes on source file with unbalanced braces due to #ifdef

I am using ctags to generate a tags file for a C project I am working on, but many functions are missing in the file. This appears to be caused by unbalanced braces in the source files due to using #ifdef. A (simplified) example:
#include <stdio.h>
struct mystruct {
long member;
#ifndef _MSC_VER
}__attribute__ ((packed));
#else /* _MSC_VER */
};
#pragma pack(pop)
#endif /* _MSC_VER */
char* greeting_text(){
return "Hello world\n";
}
int main( int argc, const char* argv[] ){
char * greeting = greeting_text();
printf(greeting);
return 0;
}
This compiles and works flawlessly with gcc -Wall under Linux. However, if I parse it using ctags problem.c, the tags file only contains entries for mystruct -- the functions are missing.
ctags --verbose reports:
OPENING problem.c as C language file
problem.c: unexpected closing brace at line 8
problem.c: retrying file with fallback brace matching algorithm
OPENING problem.c as C language file
problem.c: unexpected closing brace at line 8
so apparently ctags does not like the preprocessor tricks in the file.
Is there a way to make ctags handle this?
The manpage of ctags even explicitly mentions this problem, but indicates ctags can work around this. However, this does not appear to work...
This is with Exuberant Ctags 5.8 (Debian package 1:5.8-4).
Edit:
I'm also interested in alternatives to ctags that handle these kinds of constructs.
Because of the problems with ctags, I ended up using cscope instead.
While it's not perfect, it handles macros better than ctags, and it can integrate with vim just like ctags can (see http://vimdoc.sourceforge.net/htmldoc/if_cscop.html#:cscope ).
I would try running the preprocessor (gcc -E) on the files before giving them to ctags. Whether this will produce a good result I am not certain, but it would be worth a try. Certainly all components of your code should appear then, but will ctags recognize the other-file references that gcc leaves in the output? Not sure.
You could try to rewrite the code so that there only is one closing brace, for example:
struct mystruct {
long member;
}
#ifndef _MSC_VER
__attribute__ ((packed))
#endif
;
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
Of course, you can define some convenience macros to make it easier to read.
You could run the unifdef tool to selectively (and temporarily) replace the inactive part of the code with blank lines (unifdef -l -U_MSC_VER). The result is
#include <stdio.h>
struct mystruct {
long member;
}__attribute__ ((packed));
char* greeting_text(){
return "Hello world\n";
}
int main( int argc, const char* argv[] ){
char * greeting = greeting_text();
printf(greeting);
return 0;
}
Ctags has no problem parsing this correctly and the line numbers remain the same (important if you create ctags searches by line number):
$ cat tags
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert#users.sourceforge.net/
!_TAG_PROGRAM_NAME Exuberant Ctags //
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
!_TAG_PROGRAM_VERSION 5.6 //
greeting_text y.c /^char* greeting_text(){$/;" f
main y.c /^int main( int argc, const char* argv[] ){$/;" f
member y.c /^ long member;$/;" m struct:mystruct file:
mystruct y.c /^struct mystruct {$/;" s file:
unifdef is available on many operating systems as a package (e.g. FreeBSD, various Linux distris, Cygwin). Homepage: http://dotat.at/prog/unifdef/

Resources