Hello everyone, I would like to discuss about C99 standard and how we code it.
I didn't realize I was coding in C++ 90% of the time when I was trying to do C language in the API support. So, the first thing I want to discuss first is how we do a "class" like in struct.
void Class_Struct1_Set_Function1(int value1, int value2) {
//Do stuff here
}
void execFunction() {
int value1 = 1;
int value2 = 2;
Class_Struct1 cs1 = { Class_Struct1_Set_Function1 };
cs1.SetFunction1(value1, value2);
}
Now the question is... in what way do programmer standardize this struct class-like and the function names? Is it like...
Code:
//Struct function
void (*class_get_set_function)(...);
//"member" function
void class_get_set_function(...) {
}
or (This is what I think, except minor issue with certain unique function names.)
Code:
//Struct function
void (*get_set_function)(...);
//"member" function
void class_get_set_function(...) {
}
or...? I'm having difficulty to determine the standardize method for this. What I do know is they are lowercase plus each word has an underline in-between. I'll be graceful for your inputs.
P.S. I'm unable to use such as "get_data" in a struct's function pointer due to another name is already redefined in SQL header file.
Keywords for C99 support:
Code:
auto
break
case
char
const
continue
default
do
double
else
enum
extern
float
for
goto
if
inline
int
long
register
restrict
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
_Bool
_Complex
Imaginary
My friend gave me a feedback about "method" with initial letter m at the starting of the name. I gave it some thoughts about it and seems is acceptable to be used in a struct. So, to use in example would be:
void class_set_function(int value1, int value2) {
//Do stuff here
}
void execFunction() {
int value1 = 1;
int value2 = 2;
Class_Struct cs1 = { class_set_function };
cs1.m_set_function(value1, value2);
}
The m_ part isn't needed inside the class section since there are different class names plus not shared in a header unless needs to be. If other oppose this, please input your opinion about this. Thanks.
June 6th, 2015, 09:54 AM
JackalStomper
Re: C99 (C language) Discussion
Please don't use C OOP.
For the sake of an answer, heres how its commonly done.
There is no standard for C OOP. Because C is not an object oriented language.
What needs to be taken into account is that C "OOP" is not even OOP. Its struct oriented programming.
Lets break it down.
Some of the most common aspects of OOP:
Encapsulation
Composition/inheritance/delegation
Polymorphism
Open recursion
What does C have?
Encapsulation - No. All members and fields are public and mutable.
Composition/inheritance/delegation - Nope.
Polymorphism - Unless you count C's lack of type safety (void*) with pointers this is a no.
Open recursion - Somewhat, methods are passed an explicit "this" pointer rather than implicitly.
So why use C?
Some people claim performance. - Wrong. C OOP as its commonly written actually decreases performance!!
Why? take a look at the above code. Note how all "methods" are really just function pointers. This means for every function call during runtime the CPU must first look up the function address from a virtual method table. And THEN call it. This means useful CPU optimizations like branch prediction and caching are impossible.
And that's just runtime. A MUCH bigger impact is the compile time optimizations that are missed out by writing C like this. Just like the CPU, it's impossible for the compiler to know what function a pointer is going to call, so it can't perform any optimizations such as function inlining. This also means a lot of housekeeping instructions have to be generated for each method call.
This is akin to making a class in C++ and having every single method be virtual.
But these are all high performance problems and not very relevant to basically anything you're going to be making. And I don't mean gaming high performance, I mean billions of calls per second kind of performance.
What IS a big impact to you is the development time and code maintenance.
For example, there's a deliberate bug in the code I introduced up above. Can you find it?
The answer:
string_new is using strlen() on str, which gets the string length minus the null terminator
the code then allocates a block of memory using this length and copies the string to it. But it copies with strcpy()
This means that the string is copied including the the null terminator.
The copied string is +1 length than what was allocated. The code is now in Undefined Behavior.
In this case, the code runs fine and prints "Hello world" without an issue.
But when you call string_delete(), the program will crash due to memory corruption.
How the code should have been written:
Code:
String* string_new(const char* str)
{
String* out = (String*)malloc(sizeof(String));
int len = strlen(str) + 1;
out->_buff = (char*)calloc(len, sizeof(char));
strncpy(out->_buff, str, len);
out->get = &string_get;
return out;
}
There is a valid argument for using C though. It's (psuedo) ABI. Almost every language on the planet has bindings for C.
But that can be utilized just as easily in C++ doing the following
Code:
extern "C" {
int some_c_abi_function(int);
}
There. The rest of your program could be written entirely in C++11 and your api is still bound in C! Simple!
Now lets take a look at the C++ equivalent of the above code
Safer code, cleaner syntax, proper encapsulation, and no external memory management.
TLDR on C vs C++
Quote:
You pick C when
you need portable assembler (which is what C is, really) for whatever reason,
your platform doesn't provide C++ (a C compiler is much easier to implement),
you need to interact with other languages that can only interact with C (usually the lowest common denominator on any platform) and your code consists of little more than the interface, not making it worth to lay a C interface over C++ code,
you hack in an Open Source project (many of which, for various reasons, stick to C),
you don't know C++.
In all other cases you should pick C++.
June 6th, 2015, 10:49 AM
Btcc22
Re: C99 (C language) Discussion
Quote:
Originally Posted by JackalStomper
This means useful CPU optimizations like branch prediction and caching are impossible.
To nitpick, this is true on certain architectures (PPC, where you'll take a huge performance hit for virtual calls) but indirect branch prediction has been around for quite a while. Developers would make efforts to avoid virtual calls on the Xbox 360 because it can matter for games.
Another nitpick would be trying to catch the std::bad_alloc exception. If you're out of memory, just let the program terminate. In many cases, the allocation is going to succeed and you'll end up hitting the wall when you actually attempt to make use of the memory, as the OS tries to back the page.
June 6th, 2015, 12:37 PM
JackalStomper
Re: C99 (C language) Discussion
Quote:
Originally Posted by Btcc22
To nitpick, this is true on certain architectures (PPC, where you'll take a huge performance hit for virtual calls) but indirect branch prediction has been around for quite a while. Developers would make efforts to avoid virtual calls on the Xbox 360 because it can matter for games.
Another nitpick would be trying to catch the std::bad_alloc exception. If you're out of memory, just let the program terminate. In many cases, the allocation is going to succeed and you'll end up hitting the wall when you actually attempt to make use of the memory, as the OS tries to back the page.
Very true on both accounts! The try/catch for this particular instance is terrible, yes.
Guess its just a force of habbit from handling other things that throw in constructors. Unhandled exceptions in constructors can lead to very bad things.
June 6th, 2015, 08:49 PM
RadWolfie
Re: C99 (C language) Discussion
Quote:
Originally Posted by JackalStomper
There is no standard for C OOP. Because C is not an object oriented language.
What needs to be taken into account is that C "OOP" is not even OOP. Its struct oriented programming.
Yeah... I accidentally did that, so that's why I'm converting the interface class to struct containing function pointers in the public header files. This will reduce the exports and file size too. See far down below of my post for example I'm planning to do.
Quote:
Originally Posted by JackalStomper
Some people claim performance. - Wrong. C OOP as its commonly written actually decreases performance!!
Why? take a look at the above code. Note how all "methods" are really just function pointers. This means for every function call during runtime the CPU must first look up the function address from a virtual method table. And THEN call it. This means useful CPU optimizations like branch prediction and caching are impossible.
Um... DirectX does this same thing as I had looked in its header file how it works to execute functions. You're saying DirectX's method also decrease performance?
Quote:
Originally Posted by JackalStomper
TLDR on C vs C++
You pick C when
you need portable assembler (which is what C is, really) for whatever reason,
your platform doesn't provide C++ (a C compiler is much easier to implement),
you need to interact with other languages that can only interact with C (usually the lowest common denominator on any platform) and your code consists of little more than the interface, not making it worth to lay a C interface over C++ code,
you hack in an Open Source project (many of which, for various reasons, stick to C),
you don't know C++.
In all other cases you should pick C++.
It is needed to allow interaction with C API just like Windows did for their WINAPI. Internally still is in C++ as intended since I do need some of the C++ keywords to reduce the duplicates which serve same purpose.
Quote:
Originally Posted by Btcc22
To nitpick, this is true on certain architectures (PPC, where you'll take a huge performance hit for virtual calls) but indirect branch prediction has been around for quite a while. Developers would make efforts to avoid virtual calls on the Xbox 360 because it can matter for games.
I didn't know that, thanks for sharing with us. Luckily I'm coding hobby project(s) in Windows platform only. Plus I think Windows is not compatible with PPC architecture? Confirmed, PPC is not a x86 / x64 architecture. So that's one less thing to worry about.
Quote:
Originally Posted by Btcc22
Another nitpick would be trying to catch the std::bad_alloc exception. If you're out of memory, just let the program terminate. In many cases, the allocation is going to succeed and you'll end up hitting the wall when you actually attempt to make use of the memory, as the OS tries to back the page.
Unless you reserved a portion of memory to troubleshoot the cause? This might be helpful when using a very small reserved portion of memory to find out why it made an exception. Or perform an garbage collection to find what's need to keep and free up then try again to see if std::bad_alloc does not return.
Quote:
Originally Posted by JackalStomper
Unhandled exceptions in constructors can lead to very bad things.
Hmm... I'll keep that noted whenever it happen.
Now, for a mock up of class in struct I'm intending to do is:
Code:
typedef struct IDemo {
int (*m_get_int(IDemo*);
void(*m_set_int(IDemo*, int);
} IDemo;
That way, I can use a member function (which really is a function pointer) to operate as C++ internally. And another is non-member function, which doesn't need to include itself as it is a single class.
Now it reminds me... in C# it doesn't allow an single variable to be used as getter/setter? aka must have 1 function each? To me, it seems take more work to do just for C#. P.S. Not an expert with C#, just well enough knowledge with C++ and most asm.