Skip to content

Start implementing C wrapper #553

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

Merged
merged 21 commits into from
Jul 1, 2023
Merged

Start implementing C wrapper #553

merged 21 commits into from
Jul 1, 2023

Conversation

siiky
Copy link
Contributor

@siiky siiky commented Apr 22, 2023

This is a sketch implementation to serve as discussion point for #551.

An alternative fork can be found here, and an example program using this WIP wrapper here.

Currently, the program sometimes crashes (with a segfault or a floating point exception) and sometimes "succeeds" ("Result is invalid" is printed to stderr). I haven't discovered yet why that is because I can't make it crash inside gdb :/

For now I started with the reading API only, because that's what I had to use and know now how to use. It should be enough, at least for a while, to discuss what needs to be discussed.


As is custom in C, there's a library prefix used to try to avoid identifier collisions -- I chose zxing.

Identifiers follow an easy rule (I think): ZXing is lower-cased, and the namespace separator (::) is replaced with an underscore (_):

  • CamelCase is maintained everywhere;
  • Type names are prefixed with the library prefix (e.g. zxing_ImageFormat for ZXing::ImageFormat);
  • Enum variants are prefixed with the library prefix as well as the enum name (e.g. zxing_ImageFormat_RGB for ZXing::ImageFormat::RGB);
  • Instance methods are prefixed with the library prefix as well as the type name (e.g. zxing_ImageView_format() for ZXing::ImageView.format());
  • Other functions are only prefixed with the library prefix (e.g. zxing_ReadBarcode() for ZXing::ReadBarcode()).

I chose this naming scheme simply to make it easy to switch or find the corresponding identifiers from one to the other: someone using the C wrapper should be able to easily find the corresponding C++ identifier and vice-versa.


Some things to improve/consider/discuss:

  • Using void *
  • Better enum interoperability
  • Let C users stack allocate if possible?

Copy link
Collaborator

@axxel axxel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting to work on this. I hope my comments make sens to you.

@siiky siiky requested a review from axxel April 23, 2023 13:18
Copy link
Collaborator

@axxel axxel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@siiky siiky requested a review from axxel April 24, 2023 20:50
Copy link
Collaborator

@axxel axxel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add your sample code from the other repo as well? Do you think you could also add a CMakeLists.txt? If not, I'll try to add one to this PR myself.

@siiky
Copy link
Contributor Author

siiky commented Apr 27, 2023

Sure, I can add the example program. However, it's still not working, I need to get some time to try to debug it. NVM that, I screwed converting a PNG to JPEG and didn't notice it... The program now reports the result as "valid". Still have to write a function to get the data out, but I can add the program here.

RE CMakeLists.txt I can try too, but I'm not familiar with CMake so it'd probably be easier and faster if you added one yourself ^^'

BTW is it worth adding NULL checks to input arguments?

@siiky siiky requested a review from axxel April 27, 2023 23:00
Copy link
Collaborator

@axxel axxel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think I'm out of ideas on how to further improve this, except simply adding the missing functions.

@axxel
Copy link
Collaborator

axxel commented Apr 28, 2023

Sure, I can add the example program. However, it's still not working, I need to get some time to try to debug it. NVM that, I screwed converting a PNG to JPEG and didn't notice it... The program now reports the result as "valid". Still have to write a function to get the data out, but I can add the program here.

In that regard: note that I added a commit changing the stbi usage to be in line with the other tools. This would then also work for png images.

RE CMakeLists.txt I can try too, but I'm not familiar with CMake so it'd probably be easier and faster if you added one yourself ^^'

The following should work as a minimal starting point (not tested):

zxing_add_package_stb()

if (BUILD_READERS)
    add_executable (zxing-c-test zxing-c.cpp zxing-c-test.c)
    target_link_libraries (zxing-c-test ZXing::ZXing stb::stb)
endif()

EDIT: you also need to add the line add_subdirectory (wrappers/c) at the end of the top-level CMakeLists.txt.

BTW is it worth adding NULL checks to input arguments?

No.

@siiky
Copy link
Contributor Author

siiky commented Apr 28, 2023

This would then also work for png images.

Yeah, I know STBI works for PNGs &c, I was just trying different options and that's what I committed lol

you also need to add the line add_subdirectory (wrappers/c) at the end of the top-level CMakeLists.txt.

Added also a BUILD_C_WRAPPER option. (And I'm not really sure how to try the CMakeLists.txt, I'm generally content with Makefiles ^^)

@siiky
Copy link
Contributor Author

siiky commented Apr 28, 2023

I'll be adding shortly the means to extract the data out of a result but I have a question on "style". The two alternatives I see are:

const char* zxing_Result_data(const zxing_Result* result, int* len);

// OR

const zxing_ByteArray* zxing_Result_bytes(const zxing_Result* result);

const char* zxing_ByteArray_data(const zxing_ByteArray* bytes);
int zxing_ByteArray_size(const zxing_ByteArray* bytes);

The first approach seems more familiar to me, more C-like: the caller gets a pointer to the internal data array, but ownership is not moved. Users must be careful not to use the pointer after calling zxing_Result_delete, or vice-versa, not to call zxing_Result_delete before they no longer need the pointer.

The second approach, to get a pointer we need to copy the whole bytes (new zxing_ByteArray(std::copy(zxing_Result_bytes(result))) IIUC), which is a waste. The ByteArray and the Result may be freed independently, but it doesn't look like a much better situation... Additionally, it's a bit more code to write comparing with the first approach.

@axxel
Copy link
Collaborator

axxel commented Apr 28, 2023

If we were only concerned with the bytes() member, then I'd prefer not to copy the data. But there are other properties, especially text() which is certainly the most useful 'result' and there, this approach does not work out of the box because the std::string returned from text() is stored on the stack inside the c-getter and needs to be copied someplace. We could still attach the memory to the zxing_Result struct but it would need to be a different one than ZXing::Result. It would need to store the latter and additionally any of the std::string members once they are accessed via the c-api.

I believe it would be unwise to mix the ownership of the memory semantics for the different getters. So if we go for ownership transfer for the text() member then we should also memcpy the bytes() content to keep it consistent.

In general, I would think the API is cleaner if do not transfer the memory ownership. On the other hand: what is a c-api client going to do with the char *? If it wants to keep it around and not merely printf and forget about it, it would then likely make another copy of it, just to be able to get rid of the zxing_Result *. Then we have just another copy and did not gain anything.

I guess it would be a bit more generic and 'standard' if the returned char * memory is then owned by the client and needs to be freed.

@axxel
Copy link
Collaborator

axxel commented May 11, 2023

Did I put you off somehow, did I miss an open question your are waiting for the answer or are you simply on vacation? :)

@siiky
Copy link
Contributor Author

siiky commented May 12, 2023

Nah, don't worry, I won't run away :) I just have a ton of things atm and this isn't a priority right now.

@siiky
Copy link
Contributor Author

siiky commented May 29, 2023

@axxel Sorry I left this hanging all of a sudden. I'm hoping I'll be able to relax a bit around mid-June and have time to work on this again.

@siiky
Copy link
Contributor Author

siiky commented Jun 23, 2023

Hey @axxel, I restarted work on this just now, hoping it would be trivial to add support for the bytes() field. The code in this latest commit compiles alright, but the test program is now complaining the result is invalid (it would say valid before). I'm not sure what happened, but will need some time to figure out what's wrong.

In the meantime, could you help with adding the right CMake files to the right places? And then explain how to use them to build the example program? :D

@axxel
Copy link
Collaborator

axxel commented Jun 27, 2023

I don't see how your latest commit can have any effect on your test program. I'm pretty sure it is something else.

@axxel
Copy link
Collaborator

axxel commented Jul 1, 2023

I went ahead and added a few more properties of DecodeHints and Result. Also added a ctest case. The test program actually outputs some content now. What else would be required for your original use case?

@siiky
Copy link
Contributor Author

siiky commented Jul 1, 2023

Hey, thanks for your work! Sorry I was unavailable once again these few days, I'm going slightly insane with some unexpected events lately...

For our use case at work we only need zxing_ReadBarcode and zxing_Result_bytes/zxing_Result_text, so this wrapper is already feature-complete for us (and looking much better than my original hack).

As it is, is it ready to be merged or is there anything missing for you?

 * new BUILD_C_API cmake option
 * implement multi symbol api
 * add ContentType
 * add zxing_BarcodeFormatFromString
 * minor cleanup
@axxel
Copy link
Collaborator

axxel commented Jul 1, 2023

I just added zxing_Results_* support which I thought was still a major lack of functionality. Please note that I decided to model "not found" by returning a NULL for both single and multi-symbol variants.

I'll be happy to merge it as is and add missing stuff from there when someone speaks up. I intent to eventually simply add the zxing-c.* to the core library to be automatically available for all libZXing users. But before I do that, it would be nice to get some feedback about the usability of the API.

@axxel axxel marked this pull request as ready for review July 1, 2023 20:22
@axxel axxel merged commit 6495611 into zxing-cpp:master Jul 1, 2023
@siiky
Copy link
Contributor Author

siiky commented Jul 1, 2023

note that I decided to model "not found" by returning a NULL for both single and multi-symbol variants

Yup, seems to be the most natural.

I'll be happy to merge it as is and add missing stuff from there when someone speaks up. I intent to eventually simply add the zxing-c.* to the core library to be automatically available for all libZXing users. But before I do that, it would be nice to get some feedback about the usability of the API.

Makes sense to me. I won't be able to provide any opinion on advanced use-cases, but once I convert our program to use the new C API, I'll report any issues I find.

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

Successfully merging this pull request may close these issues.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy