• Advertisement
Sign in to follow this  

Which naming style for my C Library?

Recommended Posts

I am writing a open source single header C library and want to nail down the naming conventions.
Right now i have a mixed version, but i am not sure of people want to like this style, so i wanted to ask you if this is a good and readable style.
 
// ABC = Library Namespace
 
// Preprocessor defines:
#define ABC_MY_DEFINE_X

// Constants
#define ABC_MY_CONSTANT 42
 
// Macros:
#define ABC_MyMacro(a, b)

// Custom types
#if !defined(abc_global)
#define abc_global static
#define abc_inline inline
#define abc_internal static
#endif

#if !defined(abc_u64)
typedef unsigned long long abc_u64;
...
#endif

// Structs
typedef struct abc_MyAwesomeStruct {
  abc_u64 someValue;
} abc_MyAwesomeStruct;

// Enums
typedef enum abc_MySpecialType {
  abc_MySpecialType_First,
  abc_MySpecialType_Second,
  abc_MySpecialType_Third,
} abc_MySpecialType;

// Inline functions
abc_inline abc_f32 abcSquaredF32(abc_f32 value) {
  abc_f32 result = value * value;
  return (result);
}

// Exported Functions
abc_extern void abcMyAwesomeFunction(abc_u32 x, abc_u32 whatever);

// Function for internal use only
abc_internal void __abcMyInternalFunction(abc_u32 x, abc_u32 *ptr);

// Global variables for internal use only
abc_global abc_u32 whatEverValue = 0;

Also i use custom typedefs for integer and decimal types, is this okay? or should i use <stdint.h> only ?
Would be mapping the custom type to a stdint.h type a better way to go?

What i dont want is normal C naming conventions where everything except defines are lowercase + underscore.
Its so much nicer to read abcMyAwesomeFunction instead of abc_my_awesome_function.

I would use abc_MyAwesomeFunction, because its consistent to everything else.

What do you think? Edited by Finalspace

Share this post


Link to post
Share on other sites
Advertisement

Looks like it's on me to be the annoying guy.

 

 

Also i use custom typedefs for integer and decimal types, is this okay? or should i use only ? Would be mapping the custom type to a stdint.h type a better way to go?

 

Just use the <stdint.h> definitions. They are nice, they are clean, they are understood everywhere. It's annoying extra definitions, especially because you have to memorize the new names, and second because you (the user) never know if that name that the library developer gave is equivalent with what he/she think. For example, some libraries have 'xyzFloat', but actually it's a 'double'. Or 'xyzNumber', and who the hell will know what that is. You have to look in the headers to make sure. Also, syntax highlighters have special highlighting for 'int32_t', 'uint32_t', etc.

I've noticed an increasing awareness, for some reason, of the types defined in <stdint.h> that wasn't there before. 

 

 

What i dont want is normal C naming conventions where everything except defines are lowercase + underscore. Its so much nicer to read abcMyAwesomeFunction instead of abc_my_awesome_function.

Most C libraries will use that convention you mention you don't like. If I was a C# programmer, I would use camel-case, because it's everywhere in C#, but it would look off in C. I rarelly see camel-case style in C, and the few exceptions are from people that programmed in C# or C++ before (example).

 

 

I would use abc_MyAwesomeFunction, because its consistent to everything else.

 

I assume you are talking about the prefix. Prefix is good.

 

Continuing about typedefs. Some C programmers hate the use of typedef with structs and enums, others use it always, others are more flexible. I'm on the flexible side. If it's a game API, I will use. It's a non-critical kind of software and I want to make easier for my lazy user to declare a variable. If I'm writing a scientific software, I won't use typedef with struct or enum, as I'm worried about readability and reproducibility, and I don't give a fu*k if the other person think it's annoying to type "struct type_name variable_name;", I care about the damn readability and reproducibility. So, imo, it's double standard here.

 

Macros. Yeah, all upper-case. Macros imitating functions? I don't know if I love or I hate them. But, be careful when implementing them. Sometimes, their wrong implementation will cause the syntax that you put in them to not properly "match" the syntax of the location where the user called them. I don't have a example in hands, I just remember when I was once using one macro of one of those single-header libraries and I was getting compilation error because of how the syntax was written in the macro.

Edited by felipefsdev

Share this post


Link to post
Share on other sites

ust use the <stdint.h> definitions. They are nice, they are clean, they are understood everywhere. It's annoying extra definitions, especially because you have to memorize the new names, and second because you (the user) never know if that name that the library developer gave is equivalent with what he/she think. For example, some libraries have 'xyzFloat', but actually it's a 'double'. Or 'xyzNumber', and who the hell will know what that is. You have to look in the headers to make sure. Also, syntax highlighters have special highlighting for 'int32_t', 'uint32_t', etc.

I've noticed an increasing awareness, for some reason, of the types defined in <stdint.h> that wasn't there before.


That example about xyzFloat: If someone defines a float but uses a double as real type, its simply stupid.
But i agree to use standard types in a library.

But what about types which do not exists in the standard (Bool as 32 bit for example)?
typedef int32_t abc_b32;

Most C libraries will use that convention you mention you don't like. If I was a C# programmer, I would use camel-case, because it's everywhere in C#, but it would look off in C. I rarelly see camel-case style in C, and the few exceptions are from people that programmed in C# or C++ before (example).


That may be true, because i mostly code in object oriented languages, but i still dont like the lowercase style.
And what about SDL? SDL is C and has SDL_ as prefix and all names are camelcase as well.

Continuing about typedefs. Some C programmers hate the use of typedef with structs and enums, others use it always, others are more flexible. I'm on the flexible side. If it's a game API, I will use. It's a non-critical kind of software and I want to make easier for my lazy user to declare a variable. If I'm writing a scientific software, I won't use typedef with struct or enum, as I'm worried about readability and reproducibility, and I don't give a fu*k if the other person think it's annoying to type "struct type_name variable_name;", I care about the damn readability and reproducibility. So, imo, it's double standard here.


I hate typedefs, but using struct in C without typedef is not valid, isnt it?

This gives me a compile error in VC++ 2015 - in C Compiler not C++:
struct MyStruct {
uint32_t value;
};

But this works:

typedef struct MyStruct {
uint32_t value;
} MyStruct;

Also enums have the same problem, it requires typedef as well in C.

ust use the <stdint.h> definitions. They are nice, they are clean, they are understood everywhere. It's annoying extra definitions, especially because you have to memorize the new names, and second because you (the user) never know if that name that the library developer gave is equivalent with what he/she think. For example, some libraries have 'xyzFloat', but actually it's a 'double'. Or 'xyzNumber', and who the hell will know what that is. You have to look in the headers to make sure. Also, syntax highlighters have special highlighting for 'int32_t', 'uint32_t', etc.

I've noticed an increasing awareness, for some reason, of the types defined in <stdint.h> that wasn't there before.


That example about xyzFloat: If someone defines a float but uses a double as real type, its simply stupid.
But i agree to use standard types in a library.

But what about types which do not exists in the standard (Bool as 32 bit for example)?
typedef int32_t abc_b32;
 

Most C libraries will use that convention you mention you don't like. If I was a C# programmer, I would use camel-case, because it's everywhere in C#, but it would look off in C. I rarelly see camel-case style in C, and the few exceptions are from people that programmed in C# or C++ before (example).


That may be true, because i mostly code in object oriented languages, but i still dont like the lowercase style.
And what about SDL? SDL is C and has SDL_ as prefix and all names are camelcase as well.
 

Continuing about typedefs. Some C programmers hate the use of typedef with structs and enums, others use it always, others are more flexible. I'm on the flexible side. If it's a game API, I will use. It's a non-critical kind of software and I want to make easier for my lazy user to declare a variable. If I'm writing a scientific software, I won't use typedef with struct or enum, as I'm worried about readability and reproducibility, and I don't give a fu*k if the other person think it's annoying to type "struct type_name variable_name;", I care about the damn readability and reproducibility. So, imo, it's double standard here.


I hate typedefs, but using struct in C without typedef is not valid, isnt it?

This gives me a compile error in VC++ 2015 - in C Compiler not C++:
struct MyStruct {
uint32_t value;
};
But this works:
typedef struct MyStruct {
uint32_t value;
} MyStruct;
Also enums have the same problem, it requires typedef as well.

ust use the <stdint.h> definitions. They are nice, they are clean, they are understood everywhere. It's annoying extra definitions, especially because you have to memorize the new names, and second because you (the user) never know if that name that the library developer gave is equivalent with what he/she think. For example, some libraries have 'xyzFloat', but actually it's a 'double'. Or 'xyzNumber', and who the hell will know what that is. You have to look in the headers to make sure. Also, syntax highlighters have special highlighting for 'int32_t', 'uint32_t', etc.

I've noticed an increasing awareness, for some reason, of the types defined in <stdint.h> that wasn't there before.


That example about xyzFloat: If someone defines a float but uses a double as real type, its simply stupid.
But i agree to use standard types in a library.

But what about types which do not exists in the standard (Bool as 32 bit for example)?
typedef int32_t abc_b32;

Most C libraries will use that convention you mention you don't like. If I was a C# programmer, I would use camel-case, because it's everywhere in C#, but it would look off in C. I rarelly see camel-case style in C, and the few exceptions are from people that programmed in C# or C++ before (example).


That may be true, because i mostly code in object oriented languages, but i still dont like the lowercase style.
And what about SDL? SDL is C and has SDL_ as prefix and all names are camelcase as well.

Continuing about typedefs. Some C programmers hate the use of typedef with structs and enums, others use it always, others are more flexible. I'm on the flexible side. If it's a game API, I will use. It's a non-critical kind of software and I want to make easier for my lazy user to declare a variable. If I'm writing a scientific software, I won't use typedef with struct or enum, as I'm worried about readability and reproducibility, and I don't give a fu*k if the other person think it's annoying to type "struct type_name variable_name;", I care about the damn readability and reproducibility. So, imo, it's double standard here.


I hate typedefs, but using struct in C without typedef is not valid, isnt it?

This gives me a compile error in VC++ 2015 - in C Compiler not C++:
struct MyStruct {
uint32_t value;
};
But this works:

typedef struct MyStruct {
uint32_t value;
} MyStruct;
Also enums have the same problem, it requires typedef as well.

-> Weird forum bug while saving...

Share this post


Link to post
Share on other sites
// Custom types#if !defined(abc_global)#define abc_global static#define abc_inline inline#define abc_internal static#endif
"single-header library" is a bit ambiguous; is it a library with 1 or more C files and a single header, or is the entire library contained in the header? Assuming the latter, all your functions should be static, and maybe some should be inline too (but keep in mind "inline" is a C99 thing, so if you want to support C89 you'll need to hide it behind an ifdef). If you omit the "static" you're likely to end up with collisions if multiple files include your library.

abc_internal is pretty misleading. Usually when people see "internal" they think something marked with __attribute__((visibility("hidden"))) (GCC-like compilers); i.e., usable by the entire library/executable (not just the current compilation unit), but the symbol isn't exposed publicly to other code. I'm not sure what "global" is supposed to indicate.

#if !defined(abc_u64)typedef unsigned long long abc_u64;...#endif
That's wrong on a lot of platforms. uint64_t is unsigned long long on Windows and OS X, but it's usually unsigned long on other platforms. This is a particularly infuriating issue because they're *mostly* compatible (same size, alignment, etc.), so the compiler won't generally warn you, except for when it will (like when you're working with pointers to abc_u64 instead of an actual abc_u64).

in general you should use just use stdint.h, as others have mentioned. It's really nice to be able to use a standard name instead of having to remember a different name for each API, which may or may not be compatible with what you expect. The major exception is if you need the library to work with versions of MSVC older than 2013, since Microsoft didn't actually ship stdint.h (or a lot of other C99 features) until then. If that's the case, you should typedef your type to unsigned __int64 on MSVC, not unsigned long long. Unfortunately a lot of people stick to ancient versions… I routinely encounter people using 8.0 (circa 2005) and older. How much patience you have for them is up to you; mine is just about worn out.

For everyone else it's usually pretty safe to assume stdint.h is available, but you can always check __STDC_VERSION__ and possibly the compiler version and/or libc version. If that's still not enough, the best solution I know of is to use limits.h to try to find a type with MIN/MAX values equal to what you expect, but that opens you back up to the issue of incompatible types.

What i dont want is normal C naming conventions where everything except defines are lowercase + underscore.
Its so much nicer to read abcMyAwesomeFunction instead of abc_my_awesome_function.

I would use abc_MyAwesomeFunction, because its consistent to everything else.


One very common convention (used by glib, among lots of others), is CamelCase for types, UPPER_CASE for macros, and lower_case_with_underscores for functions. So you would have

#define ABC_FOO 1729
typedef struct { uint64_t value } AbcBar;
void abc_baz(void);
Mixing notations (i.e., abc_MyAwesomeFunction instead of AbcMyAwesomeFunction, or abc_FOO instead of ABC_FOO) is pretty uncommon (and, IMHO, very annoying).

I hate typedefs, but using struct in C without typedef is not valid, isnt it?


Sure it is, C just has different name spaces for different types of identifiers. You just need to do something like

struct MyStruct {  uint32_t value; };
void my_struct_set_value(struct MyStruct* my_struct, uint32_t value) {
  my_struct->value = value;
}
Note the "struct MyStruct" instead of just "MyStruct" on the function.

Personally, I prefer to create typedefs, but there is something to be said for avoiding them; it does help improve code readability a bit. When you see "struct Foo", "enum Bar", or "union Baz" you at least have a good idea of what to expect. That's especially true for "enum Bar" since by convention the values will start with "BAR_".

The reason I prefer typedefs is consistency; you can't really create aliases for structs in C other than typedefs, so there are situations where you have no choice but to *not* have the struct/union/enum keyword. Those cases aren't all that common, but IMHO consistency is much more important than avoiding typedefs.

Share this post


Link to post
Share on other sites

 

I hate typedefs, but using struct in C without typedef is not valid, isnt it?

It should work without typedefs. Maybe it's the situation that @nemequ mentioned. If you don't use typedef, you should write the "struct" in the variable declaration. Or, you are trying to declare one struct inside another without forward declaration. Can't know for sure without more code, but it should work.

 

That may be true, because i mostly code in object oriented languages, but i still dont like the lowercase style. And what about SDL? SDL is C and has SDL_ as prefix and all names are camelcase as well.

Yeah, naming style is pretty much an optional opinion. The number of libraries using lowercase is greater than the ones using camelcase. The only few that I know that use camelcase are SDL, GLFW and Chipmunk. If your library is good, people will use it anyway.

 

It's a good point what @nemequ raised about visual studio. I don't use VS for a long time, but I've used libraries from other people that wrote their code in VS, and was supposedly compilable on other compilers (gcc, mingw, clang), but weren't and I had to patch modifications.

Share this post


Link to post
Share on other sites

abc_internal void __abcMyInternalFunction(abc_u32 x, abc_u32 *ptr);

Identifiers starting with an underscore followed by an uppercase letter, as well as identifiers starting with a double underscore, are reserved for the implementation as defined in section 7.1.3 of the C standard. Defining any such identifiers yourself yields undefined behavior.

Share this post


Link to post
Share on other sites

My two cents, a non all upper case macro would drive me bonkers. Macros can be evil, and I like to know when I'm using them over a function.  

Share this post


Link to post
Share on other sites

Just a quick correction to this statement:

The major exception is if you need the library to work with versions of MSVC older than 2013, since Microsoft didn't actually ship stdint.h (or a lot of other C99 features) until then

stdint.h was first supported/supplied in Visual Studio 2010, so while Microsoft was behind the ball on this, it's been in there for some time now.

Edited by SBD

Share this post


Link to post
Share on other sites
Thanks for all the comments, i think i nailed it down.

I decided to use abc_ or ABC_ for everything, so its consistent. But i use CamelCase for Names because its more readable - even for macros. Also i decided to use stdint.h as well so the types are correct on all platforms.

In addition api functions are not fixed static, but rather "extern" or "static", depending on what the caller wants.
There will be no c file - just one header file including the implementation which can be enabled when needed.

This way the library can be statically linked, or used in one translation unit only, or in multiple translation units.

-> Inspired by Sean Barret
 

abc_internal void __abcMyInternalFunction(abc_u32 x, abc_u32 *ptr);

Identifiers starting with an underscore followed by an uppercase letter, as well as identifiers starting with a double underscore, are reserved for the implementation as defined in section 7.1.3 of the C standard. Defining any such identifiers yourself yields undefined behavior.


Why undefined behavior? Shouldnt there be a simple compile/linker error when there is a name conflict and thats it?
Also i prefix everything, so its unlikely that there will be any internal function/macro which has conflicts with another.

Internal stuff should be named in a way that it either does not appear in the api at all or is named so that the caller idenitifies it very easiely.

Sean Barret uses _internal at the very end of every internal function, so its totally clear this is for internal use only.
But i still think prefix internal stuff with __ instead of internal is much better. Auto-completion will only show api related functions and nothing else.
 

abc_internal is pretty misleading. Usually when people see "internal" they think something marked with __attribute__((visibility("hidden"))) (GCC-like compilers); i.e., usable by the entire library/executable (not just the current compilation unit), but the symbol isn't exposed publicly to other code.


The point is, static has more than one meaning. But internal is totally clear that this thing is for internal use only.
But when i prefix internal stuff with __ than i would simply use static or inline and dont use any "internal" at all.
 

I hate typedefs, but using struct in C without typedef is not valid, isnt it?


Sure it is, C just has different name spaces for different types of identifiers. You just need to do something like

struct MyStruct {  uint32_t value; };
void my_struct_set_value(struct MyStruct* my_struct, uint32_t value) {
  my_struct->value = value;
}
Note the "struct MyStruct" instead of just "MyStruct" on the function.


This would work, but the caller is forced to always use "struct" for the structure as well.

Like this:

struct abc_Event event;
abc_GetNextEvent(&event);

Typedef is ugly, but then you can write:

abc_Event event;
abc_GetNextEvent(&event);

Would this work?

struct abc_Event {
uint32_t value;
};
typedef struct abc_Event abc_Event;

int32_t abc_GetNextEvent(abc_Event *event); Edited by Finalspace

Share this post


Link to post
Share on other sites

Why undefined behavior? Shouldnt there be a simple compile/linker error when there is a name conflict and thats it? Also i prefix everything, so its unlikely that there will be any internal function/macro which has conflicts with another.
It's reserved namespace, which means it can be used for anything, or do anything.

If tomorrow, the C committee decides to #define _[A-Z]* and __* to nothing, they can do so. They can also silently remove the __ prefix, or silently drop functions which such a name, or whatever they like. Yes it's unlikely they will do so, but in all cases you're at the wrong end of the stick. The latter even holds if it throws an error. Basically, it would make your library unbuildable, and if unlucky, unusable, if a conflict ever arises.

Imho, picking a name convention based on an IDE doing something feels wrong, better pick a safe convention, and fix the configuration of your IDE.

Share this post


Link to post
Share on other sites

Why undefined behavior? Shouldnt there be a simple compile/linker error when there is a name conflict and thats it? Also i prefix everything, so its unlikely that there will be any internal function/macro which has conflicts with another.

It's reserved namespace, which means it can be used for anything, or do anything.
If tomorrow, the C committee decides to #define _[A-Z]* and __* to nothing, they can do so. They can also silently remove the __ prefix, or silently drop functions which such a name, or whatever they like. Yes it's unlikely they will do so, but in all cases you're at the wrong end of the stick. The latter even holds if it throws an error. Basically, it would make your library unbuildable, and if unlucky, unusable, if a conflict ever arises.
Imho, picking a name convention based on an IDE doing something feels wrong, better pick a safe convention, and fix the configuration of your IDE.


I have seen a lot libraries which uses _ for internal uses asl well, so they would crash too when that very rare case would happen. Edited by Finalspace

Share this post


Link to post
Share on other sites
... an underscore followed by an uppercase letter ...

Apparently, only _A* to _Z* are affected, which I'd guess is much more rare. But sure, if you look long enough, you'll find violations of the rules, just like people do write non-compliant <insert-language-here>-code that doesn't break today for their compiler.

Edited by Alberth

Share this post


Link to post
Share on other sites
Why undefined behavior? Shouldnt there be a simple compile/linker error when there is a name conflict and thats it? Also i prefix everything, so its unlikely that there will be any internal function/macro which has conflicts with another.

It's undefined behavior because the standard literally says so. The relevant text of section 7.1.3 is "If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by section 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined." If you don't believe me, and you don't have a copy of the standard yourself, you can reference the working draft which is 99.9% identical to the published standards except for some legal boilerplate. Section 7.1.4 contains exclusions for very specialized scenarios used to determine or require that a library function is an actual function and not a macro, and is not relevant to your case.

The compiler might emit a diagnostic (error or warning) if you do this, but it isn't required to. Your program might just fail to work entirely. "Undefined behavior" is generally included in the standard when it would be too difficult or impossible to actually detect and prevent the behavior, or when it might be useful to the implementation to have to avoid handling the case. One reason these names are reserved is that it can allow a compiler to treat certain identifiers as special intrinsics that are not actually any other C object (function, macro, et cetera) to enable certain behaviors or optimizations.

It's not about the unlikeliness of a name conflict. By naming your identifier "__abcInternal," you have invoked undefined behavior. Any program that uses your library is now undefined. That is a huge red flag for people when evaluating libraries for use.

It's true that in practice this kind of undefined behavior is very rarely a serious problem. On the other hand, it's trivial to avoid, so why intentionally make your library that much more unattractive for use over such a simple thing? You can suffix functions with "_internal" all you like.

(Note that double underscores anywhere are also reserved for C++ programs, and are also undefined behavior if you use them; your library is C so that's fine, but you may want to consider that C++ users might want to use it, so you should probably just avoid double underscores anywhere in identifier names; there are handful of additions to underscore restrictions that C++ adds that you may want to familiarize yourself with if you care about that audience).

 

I have seen a lot libraries which uses _ for internal uses asl well, so they would crash too when that very rare case would happen.

Underscore is not necessarily the problem. Leading double underscores are, and leading underscores followed by uppercase letters are the problem. Single underscores are sometimes reserved, in certain scopes.

It's true, these cases create trouble way less often than dereferencing a null pointer (also undefined behavior) does. But doing it is still wrong. Just because other programs do it and get away with it today doesn't make it safe. It's just as stupid for them to be doing it, except they're likely doing it out of ignorance. Now you know better. In a subject as filled with subjective scales as coding conventions, it seems ridiculous to dismiss one of the small handful of objective concerns.

 

 

Apparently, only _A* to _Z* are affected

The exact text of the standard is "All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use." Emphasis mine. 

Edited by Josh Petrie

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement