Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with multiple definitions on C++ #171

Open
the-mush opened this issue Sep 11, 2019 · 24 comments
Open

Problem with multiple definitions on C++ #171

the-mush opened this issue Sep 11, 2019 · 24 comments

Comments

@the-mush
Copy link

Hi, I'm using JSMN in an Arduino project using C++, including it in a header file which gets called in multiple files on the project. The only way I could get it to work is if I modify the jsmn_init and jsmn_parse by adding the inline tag (doing so seems to work ok for now). My questions are:

  • Is there a problem with this approach?
  • If not, is there a reason for these functions not to include it by default?
  • If there is, what would be the best way to include this changes in some official capacity? As it seems like a very basic problem (although very easily solvable), at least for C++ development. Maybe a small notice in the library description?
@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

Would you mind sharing exact errors you are getting? I think I see what might possibly cause the problem you are describing but I need something more to give me confidence.

@the-mush
Copy link
Author

Yes! To be more precise, I'm using Platform.IO and including JSMN from their Library Manager. The error I'm getting is:
.pio\build\firebeetle32\src\main.cpp.o: In function 'jsmn_parse': main.cpp:(.text.jsmn_parse+0x0): multiple definition of 'jsmn_parse' .pio\build\firebeetle32\src\fatherFile.cpp.o:fatherFile.cpp:(.text.jsmn_parse+0x0): first defined here .pio\build\firebeetle32\src\main.cpp.o: In function 'jsmn_init': main.cpp:(.text.jsmn_init+0x0): multiple definition of 'jsmn_init' .pio\build\firebeetle32\src\fatherFile.cpp.o:fatherFile.cpp:(.text.jsmn_init+0x0): first defined here .pio\build\firebeetle32\src\sonFile.cpp.o: In function 'jsmn_parse': sonFile.cpp:(.text.jsmn_parse+0x0): multiple definition of 'jsmn_parse' .pio\build\firebeetle32\src\fatherFile.cpp.o:fatherFile.cpp:(.text.jsmn_parse+0x0): first defined here .pio\build\firebeetle32\src\sonFile.cpp.o: In function 'jsmn_init': sonFile.cpp:(.text.jsmn_init+0x0): multiple definition of 'jsmn_init' .pio\build\firebeetle32\src\fatherFile.cpp.o:fatherFile.cpp:(.text.jsmn_init+0x0): first defined here collect2.exe: error: ld returned 1 exit status *** [.pio\build\firebeetle32\firmware.elf] Error 1

An example structure to reproduce my problem is composed of 5 files:
main.cpp - Where I have the main Arduino functions setup() and loop(). Includes only son.h
father.h - where I originally use JSMN functions and datatypes. Includes only jsmn.h
father.cpp - includes only father.h
son.h - includes only father.h
son.cpp - includes only son.h

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

I'll send a patch file in a second and please tell me if it helps

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

Here's the patch
fix_guard.txt

@the-mush
Copy link
Author

the-mush commented Sep 11, 2019

Oh, ok... sorry for the noob question, but how do I use it?

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

Considering how small the change is you can do it by hand. On line 103 after #ifndef JSMN_HEADER add a line #define JSMN_HEADER. You should end up with something like this
obraz

@the-mush
Copy link
Author

For what I understand from the patch file, you are trying to make sure that the user has defined JSMN_HEADER, and now re-reading the README I've notice the Usage section. If I follow those instructions it does seem to compile just fine now! Probably that was all I was missing?

@the-mush
Copy link
Author

What is a pity though, is that I still need to create a jsmn file in every project I need the library, just that instead of the .h file with the inline tags, now it's just an almost empty .c file.

Wouldn't this patch go against the suggestion on the Usage section? as it seems to nullify the definition outside of the library

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

Have you tested that change without your own? I feel like it might not be enough though and that it's a problem with how the library works now.

@the-mush
Copy link
Author

Ok, now just tried the change in your last comment and it does not link properly (gives exact same error as before).

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

oh nooo, I am sorry. I'm feeling quite stupid. Guess it's a terrible idea to deal with stuff like this when one's tired. Disregard what I've said so far. Just make sure that in all the files where you #include jsmn except one you do #define JSMN_HEADER before the include. This should ensure that exists only one definition of the functions and rest of your code jsut gets the declarations.

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

Make sure to revert all the code changes. I am again really sorry.

@the-mush
Copy link
Author

another detail I forgot to mention is that I'm protecting father.h and son.h just with #pragma once, if that makes any difference

@the-mush
Copy link
Author

the-mush commented Sep 11, 2019

Oh wow! Didn't notice that I can just use the define without creating the .c file. It was just that simple. Tank you for your help and get some well deserved rest 😉!

@pt300
Copy link
Collaborator

pt300 commented Sep 11, 2019

I'd say you could do

#define JSMN_HEADER
#include "jsmn.h"

in father.h and once include jsmn.h before including father.h in main.cpp

@the-mush
Copy link
Author

Maybe I would just refrase the comment:

/* Additionally, create one jsmn.c file for jsmn implementation: */ #include "jsmn.h"

To Optionally, create one jmsn.c file ...

@the-mush
Copy link
Author

I'd say you could do

#define JSMN_HEADER
#include "jsmn.h"

in father.h and once include jsmn.h before including father.h in main.cpp

It seems to work ok without including jsmn.h before father.h 🤷‍♂️🤔

@WillNilges
Copy link

A year later, this is... kinda still an issue :/

I'm working on a little util in C with multiple C files and I'm getting the same kinds of issues.

[~/Documents/Code/octo-dash-curses] [±headers✗] 
$ make -B
  CC      main.c
cc   -o build/main.o -c src/main.c
  CC      util.c
cc   -o build/util.o -c src/util.c
gcc -o octo -lcurl build/main.o build/util.o -I.
/usr/bin/ld: build/util.o: in function `jsmn_parse':
util.c:(.text+0x5a6): multiple definition of `jsmn_parse'; build/main.o:main.c:(.text+0x4d4): first defined here
/usr/bin/ld: build/util.o: in function `jsmn_init':
util.c:(.text+0xa8a): multiple definition of `jsmn_init'; build/main.o:main.c:(.text+0x9b8): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:4: octo] Error 1

here is my code if you are so inclined to view it.

@WillNilges
Copy link

Fixed it by using code from particle.

@PKD667
Copy link

PKD667 commented Aug 13, 2022

@WillNilges the link is broken , i tried to search your repo for the jsmn file but it seems like you replaced it by cjson. If you still have it can you please share your solution ?

@WillNilges
Copy link

Unfortunately, I can't say I remember much about the details of that project. I think I switched to cjson because it worked better for my needs.

I can tell you when I removed jsmn: WillNilges/octo-dash-curses@545e7f9

This could also be useful: WillNilges/octo-dash-curses@8e9def1

You might try looking around there to see if you can find it. Sorry, I shoulda hard-linked to the commit 2 years ago 😂

@PKD667
Copy link

PKD667 commented Aug 14, 2022 via email

@pt300
Copy link
Collaborator

pt300 commented Aug 15, 2022

It seems like the move to single header was not the best idea. It somewhat makes sense in small embedded projects, but seems to just cause issues in bigger projects.

Do you think it would be better to just revert to the regular source + headers system or somehow maintain both versions?

@pt300 pt300 reopened this Aug 15, 2022
@BenBE
Copy link
Contributor

BenBE commented Aug 15, 2022

It seems like the move to single header was not the best idea. It somewhat makes sense in small embedded projects, but seems to just cause issues in bigger projects.

While single-file libraries make sense with some build systems, they more often than not just are a PITA as they require to define custom compilation flags for one file, but not any other. The much simpler approach really is having the main code part of the library as part of the C files, which can then be compiled once and just linked into the final project (be it directly or indirectly using an object archive (static linking) or even as shared library (dynamic linking). This automatically resolves any symbol duplication issues that otherwise improper management of compilation flags with the single header approach may cause. Additionally, the header files are called "headers" for a reason: They should define the externally visible interface of a library, not its internal implementation.

Do you think it would be better to just revert to the regular source + headers system or somehow maintain both versions?

Yes, please have the code be a normal C file, with the header solely containing the externally visible library interface (and nothing more).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants