Results 1 to 7 of 7

Thread: C99 (C language) Discussion

  1. #1

    Lightbulb C99 (C language) Discussion

    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.

    Code:
    typedef struct {
      void (*SetFunction1)(int, int);
    } Class_Struct1;
    
    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
    Reply With Quote

  2. #2
    suum cuique sehe's Avatar
    Join Date
    Aug 2011
    Location
    Hungary
    Posts
    217

    Re: C99 (C language) Discussion

    Reply With Quote

  3. #3

    Re: C99 (C language) Discussion

    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:
    Code:
    typedef struct {
      void (*m_set_function)(int, int);
    } Class_Struct;
    
    
    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.
    Reply With Quote

  4. #4
    Slightly Insane JackalStomper's Avatar
    Join Date
    Oct 2009
    Posts
    988

    Re: C99 (C language) Discussion

    Please don't use C OOP.
    For the sake of an answer, heres how its commonly done.
    Code:
    #define _CRT_SECURE_NO_WARNINGS // msvc bullshit
    
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    typedef struct String
    {
        char* _buff;
        const char* (*get)(String*);
    } String;
    
    const char* string_get(String* self)
    {
        return self->_buff;
    }
    
    String* string_new(const char* str)
    {
        String* out = (String*)malloc(sizeof(String));
        int len = strlen(str);
        out->_buff = (char*)calloc(len, sizeof(char));
        strcpy(out->_buff, str);
        out->get = &string_get;
        return out;
    }
    
    void string_delete(String* self)
    {
        free(self->_buff);
        free(self);
    }
    
    int main(void)
    {
        String* str = string_new("Hello world");
        puts(str->get(str));
        string_delete(str);
        return 0;
    }
    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
    Code:
    #define _CRT_SECURE_NO_WARNINGS // msvc bullshit
    
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    
    class String
    {
        char* _buff;
    public:
        String(const char* str)
        {
            int len = strlen(str) + 1;
            try {
                _buff = new char[len];
                memcpy(_buff, str, len);
            } catch (const std::exception&) {
                _buff = nullptr;
            }
        }
    
        ~String()
        {
            if (_buff)
                delete[] _buff;
        }
    
        const char* get()
        {
            return _buff;
        }
    };
    
    int main()
    {
        String str("hello world");
        std::cout << str.get() << '\n';
        return 0;
    }
    Safer code, cleaner syntax, proper encapsulation, and no external memory management.


    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++.
    Last edited by JackalStomper; June 6th, 2015 at 11:02 AM.
    Reply With Quote

  5. #5
    Senior Member Btcc22's Avatar
    Join Date
    Sep 2012
    Posts
    567

    Re: C99 (C language) Discussion

    Quote Originally Posted by JackalStomper View Post
    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.
    Last edited by Btcc22; June 6th, 2015 at 12:14 PM.
    Reply With Quote

  6. #6
    Slightly Insane JackalStomper's Avatar
    Join Date
    Oct 2009
    Posts
    988

    Re: C99 (C language) Discussion

    Quote Originally Posted by Btcc22 View Post
    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.
    Reply With Quote

  7. #7

    Re: C99 (C language) Discussion

    Quote Originally Posted by JackalStomper View Post
    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 View Post
    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 View Post
    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 View Post
    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 View Post
    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 View Post
    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;
    
    main() {
      IDemo* pIDemo = init_demo();
      pIDemo->m_set(pIDemo, 30);
      int demo_int = pIDemo->m_get_int(pIDemo);
    }
    and
    Code:
    typedef struct IGlobal {
      int (*m_get_int();
      void(*m_set_int(int);
    } IDemo;
    
    main() {
      IGlobal* pIGlobal = get_global();
      pIGlobal->m_set(10);
      int global_int = pIGlobal->m_get_int();
    }
    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.
    Reply With Quote

Thread Information

Users Browsing this Thread

There are currently 2 users browsing this thread. (0 members and 2 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •