nancygold's Journal
 
[Most Recent Entries] [Calendar View] [Friends View]

Tuesday, July 23rd, 2024

    Time Event
    12:38a
    Quikest way to implement C++ style OOP in C99
    Sometimes it is useful to pack functions and related data into some single struct.
    Yet without language support it can be a bit tedious.
    Here is a set of macros to do that easier than in C++.
    To implement a template class, just put your class code inside the header, and then
    #define CLASS
    just before including it.

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    #define NOOP_IMPLEMENTATION
    #include "noop.h"
    
    
    #define CLASS CObj
    
    METHODS
      void (*foo)(int a);
      int (*bar)(int a);
      CObj *(*set_xy)(int nx, int ny);
    
    BDATA
      int x;
      int y;
    EDATA
    
    METH(void,foo,int a)
      printf("foo a=%d x=%d y=%d\n", a, this.x, this.y);
      printf("self.bar returned %d\n", self.bar(a+3));
    
    METH(int,bar,int a)
      printf("bar a=%d x=%d y=%d\n", a, this.x, this.y);
      return a * 2;
    
    METH(CObj*,set_xy,int nx, int ny)
      this.x = nx;
      this.y = ny;
      return &this;
    
    CTOR
      o->x = 123;
      o->y = 456;
    
    END
    
    
    int main(int argc, char **argv) {
      CObj *a = new(CObj);
      CObj *b = new(CObj);
      $(a).set_xy(111,333);
      printf("a...\n");
      $(b).foo(69);
      printf("b...\n");
      $(a).foo(7);
      return 0;
    }
    

    #ifndef NOOP_H
    #define NOOP_H
    extern void *gthis_;
    
    #define new(type) type##_init(malloc(sizeof(type)))
    
    #define $(o) ((gthis_=(o)), *((o)->vtbl_))
    #define self $(this_)
    #define this (*this_)
    
    #define PRFX_(prfx,mid,name) prfx##mid##name
    #define PRFX(prfx,mid,name) PRFX_(prfx,mid,name)
    
    #define METHODS typedef struct CLASS CLASS; typedef struct {
    
    #define BDATA } PRFX(CLASS,_,CVtbl); struct CLASS {CObj_CVtbl *vtbl_;
    #define EDATA }; PRFX(CLASS,_,CVtbl) PRFX(CLASS,_,vtbl_); \
      void PRFX(CLASS,_,dummy_)() {
    
    #define METH(type, name, ...) } \
      type PRFX(CLASS,_,name) (__VA_ARGS__); \
      __attribute__((constructor)) void PRFX(CLASS,__init__,name)() { \
        printf("initing %s\n", #name); \
        PRFX(CLASS,_,vtbl_).name = &PRFX(CLASS,_,name); \
      } \
      type PRFX(CLASS,_,name) (__VA_ARGS__) { CLASS *this_ = gthis_;
    
    
    #define MINIT(name) PRFX(CLASS,_,vtbl_).name = PRFX(CLASS,_,name)
    #define CTOR } CLASS *PRFX(CLASS,_,init)(CLASS *o) \
      { o->vtbl_ = &PRFX(CLASS,_,vtbl_);
    
    #define END }
    
    
    
    
    #ifdef NOOP_IMPLEMENTATION
    void *gthis_;
    #endif
    
    #endif
    


    Current Mood: amused
    12:59p
    How efficient is ECS?
    Well, we all know that doing
    
      struct agent_t {
        int guid;
        int type;
        bool is_ally;
        bool is_herbivore;
        ...
      }
    
    Is a really bad idea, since the bools will waste a lot of memory and to do
    analytical operations (i.e. listing all matching objects) you have to go through
    every object. So even if the struct is heavily packed into a bit stream, and
    your CPU has bit unpacking support, it is still super memory intensive.
    
    Now storing separate columns allows packing bools as actual bits.
    Therefore ECS reduces the memory requirement to store the same data.
    
    There is of course the question of mapping from, the agent_t.guid to
    the bit, when only part of the agents have these field.
    That mapping is probably the hardest part about ECS or columnar DBs generally.
    But it is solvable with a page table and allocating guids for specific types
    in blocks.  Then the lookup comes down to something like:
    
      uint64_t index = guid - min;
      return index < size ? data[index] : NIL;
    
    Yet since allocation happens in blocks, we can pack adjanced bits with
    1d version of a sparse voxel tree. Voila! We have less than 1 bit per field!
    It is also possible to iterate over all these items without going through
    a huge bit array. I.e. if you allocated 1024 carnivores in a row, you've just
    compressed 1024 bits into 1!
    
    Although implementing such trees efficiently is non-trivial and requires
    unrolling the recursion. Yet that is probably the most efficient way to
    implement set theoretic structures in your program, and games are all about
    set theoretic relationships. So it is a core data structure, you have to
    implement in the best way possible. And ECS opens a way to do that.


    Current Mood: amused
    3:31p
    GCC
    Apparently one has to put volatile into the
    jmp_buf * volatile pjb;
    , when optimizations are enabled, since GCC is too stupid to recognize setjmp related paraphernalia. I had hard to track GC memory corruption, which came from longjmp

    Current Mood: amused

    << Previous Day 2024/07/23
    [Calendar]
    Next Day >>

About LJ.Rossia.org