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

Improve c++ compatibility of htslib header files #772

Closed
wants to merge 2 commits into from

Conversation

daviesrob
Copy link
Member

Allow room in kputuw_dig2r for the nul byte on the end of the string used to initialize it. gcc doesn't mind it being dropped but g++ complains.

Fix missing stdint.h limit macros when including htslib/kstring.h and htslib/vcf.h in a c++ compilation. The C99 standard says c++ implementations should only define them if __STDC_LIMIT_MACROS
is also defined. C++11 later put them back, but older versions of libc may not provide them without this.

As we can't be sure we're the first to include stdint.h, defining it in our header files may be too late. Instead suitable definitions are added for the macros that are used by each header.

Fixes #771 (kstring.h error: initializer-string for array of chars is too long)

@jmarshall
Copy link
Member

The C99 standard was full of crap when it tried to tell the C++ committee what to do, so the comment doesn't need to be so detailed — at this point, disabling these macros in the absence of __STDC_LIMIT_MACROS is just a bug in old compilers/C libraries.

What about just avoiding using INTx_MAX etc in HTSlib's public header files? Certainly for the vcf.h ones, just writing out the numerical values in #define bcf_int8_vector_end etc wouldn't be much of a loss.

Defining SIZE_MAX itself will lead to ill-formed programs if something includes <stdint.h> after "htslib/kstring.h". It's a somewhat theoretical problem (you currently get warnings only if you're using -Wsystem-headers, which most people won't do), but could be avoided by instead defining a local name within the header:

#ifdef SIZE_MAX
#define SIZE_MAX_KS SIZE_MAX
#else
#define SIZE_MAX_KS ((size_t) -1)
#endif// Use SIZE_MAX_KS within htslib/kstring.h#undef SIZE_MAX_KS  // Undef it at the end of the header file

(Undeffing it afterwards so that other code (especially third-party code) just uses the standard nomenclature.)

@daviesrob
Copy link
Member Author

Yes, the vcf.h bit is quite ugly, so it might be better if it defined its own MAX and MIN macros.

I don't see why including stdint.h again should be a problem. htslib/kstring.h will already have included it, and we only create our own SIZE_MAX if stdint.h failed to make one. As we've already included stdint.h, it shouldn't do anything if included again later.

I'd rather not have to jump through hoops in the rest of the file, just because of an ancient c/c++ incompatibility.

@jmarshall
Copy link
Member

jmarshall commented Sep 25, 2018

I don't see why including stdint.h again should be a problem. htslib/kstring.h will already have included it

Oops — you are quite right that <stdint.h> was never likely to operate like <assert.h>/NDEBUG despite the C people asserting that it had a somewhat similar control macro.

It would make me nervous in general to define names that are reserved to the compiler/standard library, especially in a public header, but YMMV.

Allow room in kputuw_dig2r for the nul byte on the end of the
string used to initialize it.  gcc doesn't mind it being dropped
but g++ complains.

Avoid INTx_MIN, INTx_MAX and SIZE_MAX macros from <stdint.h> in
htslib/*.h header files.  Old versions of glibc (before 2.18), and
possibly other libc implementations don't automatically supply them
when compiling as c++.  Easiest solution is to not use them in
these headers.
(See https://sourceware.org/bugzilla/show_bug.cgi?id=15366)

Fixes samtools#771 (kstring.h error: initializer-string for array of chars
is too long)
@daviesrob
Copy link
Member Author

Mk.2 version (just pushed) avoids the problem by not using SIZE_MAX at all. According to Godbolt, the new version of the overflow checks in kstring.h may actually be a bit more efficient.

@jmarshall
Copy link
Member

Nice 😄

Now that they exist, you might as well use BCF_MIN/MAX_BT_INT* in vcf.c's bcf_enc_vint() too.

vcf.c Outdated
@@ -1731,7 +1731,7 @@ int vcf_hdr_write(htsFile *fp, const bcf_hdr_t *h)

void bcf_enc_vint(kstring_t *s, int n, int32_t *a, int wsize)
{
int32_t max = INT32_MIN + 1, min = INT32_MAX;
int32_t max = BCF_MIN_BT_INT32 + 1, min = BCF_MAX_BT_INT32;
Copy link
Member

Choose a reason for hiding this comment

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

These are boundaries on the unencoded int32_t values, so should just be

int32_t max = INT32_MIN, min = INT32_MAX;

and on the lines below you need to lose the + 8.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for spotting that. I was under the impression they were match for match.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks — looks good to me now.

@valeriuo
Copy link
Contributor

Closed by 57fe419 and e85b599.

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.

3 participants