LINUX.ORG.RU

[C] как сделать красиво?


0

0

имеется структура

struct A {
    char * text;
    int width;
    int hight;
    ...
    и т.д
}

требуется присвоить значение определённому полю структуры в зависимости от значения переменной(для примера val). Например, если val == «text» , то значение записываем в поле text структуры, если val == «width» то значение заносится в поле width. Это можно сделать примерно следующим образом:

if(strcmp(val , "text") == 0)
    A->text = "some text";
else
    if(strcmp(val , "width") == 0)
        A->width = 300;
    else
        if(strcmp(val , "hight") == 0)
            A->width = 100;

и т.д Всё будет работать, но вопрос в том, как сделать такое красиво. Не хочется чтобы в коде было куча if/else, полей таких больше десятка. Пытался сделать макросом типа:
#define SET_ATTR_VALUE(stuct, attr, value) (struct->##attr = value;)
Но компилятор ругался, типа не может найти attr и не знает что такое struct, хотя в коде подставил SET_ATTR_VALUE(A, val, «some text»);

★★★★★

У тебя проблема в том, что макросы раскрываются на этапе компиляции, а не на этапе исполнения, как ты этого хочешь.

catap ★★★★★ ()

Т.е. код

[code=cpp] #define SET_ATTR_VALUE(stuct, attr, value) (struct->##attr = value;)

SET_ATTR_VALUE(A, val, «some text»); [/code]

раскроется в:

[code=cpp] (struct-> val = «some text»;); [/code]

т.е. этот код будет эквивалентен [code=cpp] *(((u_char *)struct) + offsetof(struct A, val)) = «some text» [/code]

А у твоей структуры нет поля val, и ofsetoff не сможет посчитать смещение на этапе компиляции.

Из-за этого тебе и не дали.

catap ★★★★★ ()

типичная задача для С++ :), для С пожалуй стоит сделать так:

if( !val )
   return;

switch( *val )
{
    // "height"
    case 'h': A->height = 100; break;
    // "width"
    case 'w': A->width = 100; break;
    // "p1" + "p2"
    case 'p':
        if( *(val+1) == '1' ) A->p1 = 1;
        else A->p2 = 1;
    ...
}

если надо скорость и нет задачи проверять, что в val левое значение

ahonimous ()
Ответ на: комментарий от ahonimous

> и нет задачи проверять, что в val левое значение

причем этот код безопасен - т.к. даже если строка меньше чем надо, то мы получим завершающий \0, с которым и будем сравнивать

ahonimous ()
Ответ на: комментарий от ahonimous

> и нет задачи проверять, что в val левое значение

А зачем мучатся с строками, если мы на них кладем все равно?

catap ★★★★★ ()

на стандартном Си красиво сделать нельзя. Можно попробовать с использованием __typeof__, но это не переносимо. Можно попробовать кодогенерацию, но одна мелкая задача этого не стоит.

tailgunner ★★★★★ ()
Ответ на: комментарий от catap

> А зачем мучатся с строками, если мы на них кладем все равно?

затем что строки могут быть получены из файла с описанием данных

ahonimous ()

Я бы на С сделал что-то вроде

enum types
{
  text,
  width,
  height,
  ...
}

int comapre(char *str)
{
  if(!strcmp(str,"text"))
    return text;
  if(!strcmp(str,"width"))
    return width;
  ...
}

//в основном коде

switch (compare(str))
{
  case text:
     ....
     break;
  case width:
     ...
     break;
  ...
  default:
    ...
}
Delirium_veritas ()

Кстати, использование variant-подобных типов в C всегда гемор.

Delirium_veritas ()
Ответ на: комментарий от Delirium_veritas

это более красивое решение чем мое, но в худшем случае для перевода строки в тип будут вызваны strcmp для всего известного списка

ahonimous ()
Ответ на: комментарий от Delirium_veritas

enum types 
{ 
  text, 
  width, 
  height, 
  ... 
} 

struct s {
	const char *name;
	types num;
};
struct s s_array[] = {
	{"types", text},
	{"width", width},
	{"height",  height},
....
	{NULL, 0}
};
int comapre(char *str) 
{ 
//цикл
} 
...
switch (compare(str)) 
{ 
  case text: 
     .... 
     break; 
  case width: 
     ... 
     break; 
  ... 
  default: 
    ... 
} 

rg-400 ()

на си красиво не бывает

anonymous ()
Ответ на: комментарий от ahonimous

можно и без qsort, если самостоятельно отсортировать :)

rg-400 ()
Ответ на: комментарий от Delirium_veritas

> А надо ли их добавлять?

определенно надо такое предусматривать

ahonimous ()
Ответ на: комментарий от ahonimous

Ну тогда, ой. Но всё таки это имеет смысл, если парсить надо много вариантов параметров (явно не 3-4). На числе вариантов до 10 особо прироста производительности заметно не будет. Имхо, естественно.

Delirium_veritas ()
Ответ на: комментарий от rg-400

Боже, ужас какой. А это только на Си такая кочерга выходит?

На питоне, например, можно ли по содержанию строковой переменной сразу обратиться к нужному методу/свойству?

mclaudt ()
Ответ на: комментарий от mclaudt

> На питоне, например, можно ли по содержанию строковой переменной сразу обратиться к нужному методу/свойству?

Можно. Но, так же, как в Си, лучше этого не делать.

tailgunner ★★★★★ ()
Ответ на: комментарий от rg-400

Интересные решения. Попробую обрисовать ситуацию, в которой я всё это использую, может можно вообще отказаться от структур и сделать всё проще... Есть текстовый файл примерно такого содержания:

+window:
    text = Test window
    width = 300
    hight = 100
    +button
        text = Click me
        width = 50
        height = 20
        x = 50
        y = 20
    +textbox
        text = Simple text
        width = 100
        height = 20
        x = 50
        y = 50
Программа считывает его и соответственно строит виджеты. Для описания каждоговиджета в программе есть структура, которая хранит поля - все возможные теги, которые могут быть описаны в файле(т.е у структуры есть поле text, width, x, y и т.д) также структура хранит указатель на такую же структуру(родитель) т.е получается типа списка. Сделал я так для того чтобы можно было рекурсивно вызвать функцию построения интерфейса(начиная с последнего в списке, строим элемент, потом берём его родителя, строим его и на нём располагаем дочерний элемент). Т.е сначала считываем файл, заполняем список структур потом вызываем функцию построения которая берёт виджет из списка, берёт его тип(window, button, text и т.д) и строит его.

xterro ★★★★★ ()
Ответ на: комментарий от Delirium_veritas

По-твоему, невозможность такого вызова как-то связана с ассемблером? Это же совсем низкоуровневая вещь, которая ни про методы, ни про объектность вообще не догадывается.

Вот выше ответили, что на питоне, оказывается, возможно.

mclaudt ()
Ответ на: комментарий от xterro

Кто генерит файл? Если руками, то стоит использовать мой (и развитый) вариант с strcmp. Если софтиной (структура гарантирована), то тогда вариант ahonimous

Delirium_veritas ()
Ответ на: комментарий от mclaudt

>>лучше этого не делать

Но поцчему?

Потому что код запутывается. Не говоря о том, что обычно это не нужно (а когда нужно - делайте уже нормальную рефлексию).

Опасность подмены значения переменной и вызов нужного хацкеру метода?

Это тоже.

tailgunner ★★★★★ ()

сдесь вопрос в скорости - если параметров немного - то проще и быстрее простая проверка

ae1234 ★★ ()
Ответ на: комментарий от Delirium_veritas

Логично, но для небольших диалогов с возможностью «кастомизации» думаю подойдёт, тем более что планируется что файлик будет правится дезигнером(структура предполагается единой - есть главное окно, а на нём лежат кнопочки, чекбосики и т.д)

xterro ★★★★★ ()
Ответ на: комментарий от xterro

> Попробую обрисовать ситуацию, в которой я всё это использую, может можно вообще отказаться от структур и сделать всё проще...

Генери код автоматически - по выхлопу чего-нибудь вроде readelf, gccxml или pycparser.

tailgunner ★★★★★ ()
Ответ на: комментарий от rg-400

Писал такое в свое время. Элегантно, но плохо сопровождаемо. В итоге скатилось всё до указателей на void *func(void **); А все потому, что работа велась командой и без чёткого ТЗ.

Delirium_veritas ()
Ответ на: комментарий от xterro

Почти ООПненько получается Разрабатывается структура, которая содержит сопсно данные и несколько функций заданной структуры, которые с этими данными (и не только) что либо делают. Функции пишутся отедльно (у нас они вообще лежали в динамически подключаемых .ld'шках). В коде пишется что-то вроде some_struct->fill_data_func=ext_fill_data; После этого, для заполнения данных этой структуры достаточно сделать some_struct->fill_data(some_params...). Вот как-то так.

А. Еще.

struct {
...
ret_type(* fill_data)(params_types...)
} some_struct;
Delirium_veritas ()
Ответ на: комментарий от rg-400

Наоборот... хочется сделать простенький генератор интерфейса с дезигнером. Вот обдумываю идею реализации, чтоб «бацко» было )))

xterro ★★★★★ ()
Ответ на: комментарий от xterro

>хочется сделать простенький генератор интерфейса с дезигнером

Тогда лучше завести тулзу, которая из файла интерфейса делает готовый текст сишной программы, в котором всякие width и прочие выставляются. glade, например, так и работает, хотя там есть опция загрузки интерфейса в runtime, но, как я понимаю, для этого используется механизм свойств GObject'а.

linuxfan ()
Ответ на: комментарий от Delirium_veritas

Действительно, почти ООП... я тут подумал над кодом от rg-400, если сделать в структуре, описывающей виджет массив всех возможных атрибутов:


struct s { 
   const char *name; 
   types num; 
}; 
struct s s_array[] = { 
   {"types", text}, 
   {"width", width}, 
   {"height",  height}, 
.... 
   {NULL, 0} 
}; 

Моя структура

typedef struct _widget_s {
  _widget_s * parent;
  struct s s_array[]; 

} widget_s;

тогда можно будет по номеру найти соответствующее «поле» и присвоить ему значение...

xterro ★★★★★ ()
Ответ на: комментарий от linuxfan

Прикол в том что эти «диалоги» будут храниться в БД и на лету строиться у клиента. При этом хочется «полегче и поменьше места»

xterro ★★★★★ ()

struct A { 
    char * text; 
    char * caption; 
    int width; 
    int hight; 
    int x;
    int y;
} 

void setInt(A * a, char * field, int value) {
    if (strcmp(field,"width")==0) a->width = value;
    if (strcmp(field,"height")==0) a->height = value;
    if (strcmp(field,"x")==0) a->x = value;
    if (strcmp(field,"y")==0) a->y = value;
}

void setString(A * a, char * field, char * value) {
    if (strcmp(field,"text")==0) a->text = value;
    if (strcmp(field,"caption")==0) a->caption = value;
}

void main() {
    A obj;
    setInt(&obj,"width",100);
    setInt(&obj,"height",200);
    setInt(&obj,"x",300);
    setInt(&obj,"y",400);
    setString(&obj,"text","Some text");
    setString(&obj,"caption","Some caption");
}
no-dashi ★★★★★ ()
Ответ на: комментарий от no-dashi

Спасибо всем, почерпнул много нового... пойду мозговать :)

xterro ★★★★★ ()
Ответ на: комментарий от mclaudt

На питоне, например, можно ли по содержанию строковой переменной сразу обратиться к нужному методу/свойству?

a = dict()
def func1(): return 1
def func2(): return 2
a['one'] = func1
a['two'] = func2
s = 'two'
print a[s]()

только это тот же самый switch

dimon555 ★★★★★ ()

Proof-of-concept

/* label_fields.h */

FIELD(int, x)
FIELD(int, y)
FIELD(char *, text)
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

struct field_desc {
	const char *name;
	size_t offset;
};

size_t get_offset(struct field_desc *fields, const char *name) {
	while (fields->name) {
		if (!strcmp(fields->name, name)) {
			return fields->offset;
		}
		++fields;
	}

	printf("Invalid field name: %s", name);
	exit(EXIT_FAILURE);
}

#define SET_VALUE(widget, name, value) \
	*(typeof(value) *)((char *)(widget) + get_offset(widget##_fields, name)) = (value);

struct label {
#define FIELD(type, name) type name;
#include "label_fields.h"
#undef FIELD
};

struct field_desc label_fields[] = {
#define FIELD(type, name) {#name, offsetof(struct label, name)},
#include "label_fields.h"
#undef FIELD
	{NULL}
};

int main(void) {
	const char *text = "Hello World!";

	struct label *label = malloc(sizeof(*label));
	SET_VALUE(label, "x", 640);
	SET_VALUE(label, "y", 480);
	SET_VALUE(label, "text", text);

	printf("%d %d %s\n", label->x, label->y, label->text);

	free(label);
	return EXIT_SUCCESS;
}

Proof-of-concept для DRI-подхода. Компилируется с расширениями GCC и даже работает. Но очень надеюсь, что никто никогда так делать не будет.

ntp ()
Ответ на: комментарий от Love5an

Здесь самое место коммонлиспу, а не плюсам, или какому подобному говну.

(defclass thing ()
           ((x :initarg :x :accessor thing-x)
            (y :initarg :y :accessor thing-y)))
=>  #<STANDARD-CLASS THING 250020173>
 (defmethod (setf thing-x) :before (new-x (thing thing))
   (format t "~&Changing X from ~D to ~D in ~S.~%"
           (thing-x thing) new-x thing))
 (setq thing (make-instance 'thing :x 0 :y 1)) =>  #<THING 62310540>
 (with-slots (x y) thing (incf x) (incf y)) =>  2
 (values (thing-x thing) (thing-y thing)) =>  1, 2
 (setq thing1 (make-instance 'thing :x 1 :y 2)) =>  #<THING 43135676>
 (setq thing2 (make-instance 'thing :x 7 :y 8)) =>  #<THING 43147374>
 (with-slots ((x1 x) (y1 y))
             thing1
   (with-slots ((x2 x) (y2 y))
               thing2
     (list (list x1 (thing-x thing1) y1 (thing-y thing1)
                 x2 (thing-x thing2) y2 (thing-y thing2))
           (setq x1 (+ y1 x2))
           (list x1 (thing-x thing1) y1 (thing-y thing1)
                 x2 (thing-x thing2) y2 (thing-y thing2))
           (setf (thing-x thing2) (list x1))
           (list x1 (thing-x thing1) y1 (thing-y thing1)
                 x2 (thing-x thing2) y2 (thing-y thing2)))))

вот это как раз пример нечитабельного говна, а на С++ можно написать две строки:

map<string,variant> props;
props[ "width" ] = 100;

вместо той галиматьи, на которую ты привел ссылку

ahonimous ()
Ответ на: комментарий от ahonimous

Ты совсем, блять, наркоман, чтоли?

Ты, во-первых, где это там увидел? Во-вторых, это не аналог.

(setf *props* (make-hash-table)
      (gethash 'width *props*) 100)
Love5an ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.