1. C++ / Говнокод #2613

    +66.9

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    // Компилировать: g++ -o test reghead.cpp object1.cpp regtail.cpp main.cpp
    
    //================== object.h: ==================
    struct Object {
    	const char *name;
    	unsigned char props[0];
    };
    
    extern Object *head, *tail;
    enum {PROPTYPE_BYTE = 0x40, PROPTYPE_WORD = 0x80};
    enum {PROP_DESC = 1, PROP_LASTSTRING, PROP_COLOR, PROP_MASS, PROP_END = 0};
    
    #define DECLARE_OBJECT(name) \
    extern Object Object_##name, *name##_reg; \
    Object *name##_reg __attribute__((section("oreg"))) = &Object_##name;\
    Object Object_##name = {#name}; \
    namespace name##_props {
    
    #define PROP_BYTE(id, value) unsigned char id##_1 = id | PROPTYPE_BYTE, id##_2 = value;
    #define PROP_WORD(id, value) unsigned char id##_1 = id | PROPTYPE_WORD, id##_2[2] = {value & 0xff, value >> 8};
    #define PROP_STRING(id, value) unsigned char id##_1 = id; unsigned char id##_2[] = value;
    #define END_PROPS unsigned char final = PROP_END; }
    
    //================== reghead.cpp: ==================
    #include "object.h"
    Object *head __attribute__((section("oreg"))) = 0;
    
    //================== regtail.cpp: ==================
    #include "object.h"
    Object *tail __attribute__((section("oreg"))) = 0;
    
    //================== object1.cpp: ==================
    #include "object.h"
    
    DECLARE_OBJECT(object1)
    PROP_BYTE(PROP_COLOR, 14)
    PROP_WORD(PROP_MASS, 300)
    PROP_STRING(PROP_DESC, "description")
    END_PROPS
    
    //================== main.cpp: ==================
    #include <stdio.h>
    #include <string.h>
    #include "object.h"
    
    int main()
    {
    	Object **obj = &head;
    	while (++obj <= &tail) {
    		if (*obj == NULL)
    			continue;
    		printf("Object: %s\n", (*obj)->name);
    		unsigned char *props = (*obj)->props;
    		int id;
    		while ((id = *props) != PROP_END) {
    			if (id < PROP_LASTSTRING) {
    				char *value = (char *)props+1;
    				props = (unsigned char *)value + strlen(value) + 1;
    				switch (id) {
    				case PROP_DESC:
    					printf("    Description: %s\n", value);
    					break;
    				}
    			} else {
    				int value;
    				switch (id >> 6) {
    				case 1:
    					value = *((unsigned char *)(props+1));
    					props += 2;
    					break;
    				case 2:
    					value = *((unsigned short *)(props+1));
    					props += 3;
    					break;
    				}
    				switch (id & 63) {
    					case PROP_COLOR:
    						printf("    Color: %d\n", value);
    						break;
    					case PROP_MASS:
    						printf("    Mass:%d\n", value);
    						break;
    				}
    			}
    		}
    	}
    }

    Прошу прощения за длинный пример, но не мог не поделиться. Написано по мотивом кода из того же ZDaemon – примерно так там задаются и обрабатываются свойства объектов. Работает только с компилятором GCC. Если вы поняли, как это работает, а также почему подобный говнокод работает с gcc версии 3.2 и может зависать с более поздними версиями – можете по праву гордиться собой.

    Запостил: Arseniy, 13 Февраля 2010

    Комментарии (11) RSS

    • Я не могу собой гордится. Обьясни, пожалуйста, что он делает и почему зависит от компилятора?
      От компилятора зависит из-за этого
      __attribute__((section("oreg")))
      ?
      Ответить
      • > От компилятора зависит из-за этого
        > __attribute__((section("oreg")))

        То, что это работает только с GCC – от этого. Что касается зависимости от версии – этот код рассчитывает на то, что инициализированные глобальные переменные располагаются в памяти подряд в порядке объявления, так вот есть подозрение, что в более новых GCC это бывает не так.

        Основная задача примерно такая: имеется множество объектов, каждый из которых обладает набором свойств разных типов. Надо всё это описать на C++, причём каждый объект в отдельном файле, а потом пройтись по всем объектам и по всем свойствам каждого объекта.

        Это делается так: описание объекта – экземпляр структуры Object, инициализированная глобальная переменная (строка 16). Сами же свойства (каждое из которых характеризуется типом – байт, слово или строка, идентифиактором – что за свойство и значением) хранятся в массиве из байт. Массив, по замыслу, находится в памяти сразу за экземпляром структуры. Формируется он путем объявления множества инициализированных глобальных переменных типа unsiged char или char []. Описание свойства: первый байт – старшие два бита отвечают за тип, остальные за идентификатор (0 означает конец списка); дальше – значение.

        Чтобы пройтись по всем объектам, для каждого из них дополнительно объявляется глобальная переменная, инициализированная указателем на экземпляр структуры (строка 15). Причём они все помещаеются в отдельный сегмент данных (то самое __attribute__). Кроме того, в файлах reghead.cpp и regtail.cpp объявляются еще два указателя в том же сегменте. Значения всех этих указателей будут располагаться в памяти подряд (возможно, с заполненными нулями промежутками, учтенными в строках 50-51). Причём, если при сборке скармливать компилятору файлы именно в таком порядке, как указано (!!!), то значение указателя head будет располагаться в памяти перед всеми остальными, а tail – после всех остальных.
        Ответить
        • Феерично!!!
          Ответить
        • "глобальные переменные располагаются в памяти подряд в порядке объявления"

          GNU ld вроде как бы от балды менять объекты местами не будет...

          а как data driven система определения объектов может даже и работать, если например object1.cpp генерируется динамически из какой базы/etc.
          Ответить
    • Круть!
      Ответить
    • Где здесь с++? Автора минусуем

      PS // это С99

      PSS А namespace это #define namespace
      Ответить
      • >>g++ -o test reghead.cpp object1.cpp regtail.cpp main.cpp
        Те этого мало?? Да если "с дефайнами", то на половину говнокодов из питона, руби, сишарпа, делфи и т.д. можно говорить, что это си с препроцессором.
        Ответить
        • Последнее время все друг друга подьёбывают. Так, что он прав. ;)
          Ответить
        • да. этого, мало.

          код - прямой с99 с GNU расширениями.

          насколько долго код использует POD (plain old data), C++ не тригирется вообще.

          разница будет только на бинарном уровне и тривиальная: mangled имена и код для обработки эксепшенов.
          Ответить
          • Если честно, C++ я выбрал на автомате только по той причине, что ZDaemon на плюсах ("настоящих", "с классами"). Хотя в приведённом виде gcc его как C всё равно не жуёт из-за использования Object вместо struct Object :)
            Ответить
      • 1.) Я не вижу в приведённом коде никаких "#define namespace", в то время, как похоже, что выложили полный исходный код;
        2.) Файлы *.cpp (вы удивитесь, но "gcc *.cpp" скорее всего выдаст ошибку) и компилятор g++.
        3.) И самый главный аргумент, что, млять, если бы автор закинул бы код в раздел "Си", то автора опять обкладывали бы всякие анонимусы, ибо очевидно, что код C++. Т.е. в данной ситуации как ни крути, анонимусы вроде вас будут возмущаться.
        Ответить

    Добавить комментарий