Показаны сообщения с ярлыком Vala. Показать все сообщения
Показаны сообщения с ярлыком Vala. Показать все сообщения

В позапрошлом посте рассказывалось о создании экземпляров объектов GObject по имени класса, где, кроме всего прочего, было сказано:
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type... и вызова этого метода.
Все почти так и есть, но в коде GtkBuilder случайно был найден довольно изящный и портабельный механизм поиска foo_bar_get_type. Идея простая: нужно создать объект GModule не для динамически загружаемого модуля, а для самой программы. Для чего в метод g_module_open(const gchar *file_name, GModuleFlags flags) вместо имени файла нужно передать NULL:
file_name — the name of the file containing the module, or NULL to obtain a GModule representing the main program itself.
Пример на Vala на основе идей из GtkBuilder (механизм получения типа по имени класса иллюстрирует функция type_from_name_lazy):
public abstract class Animal : Object
{
  public abstract string to_string();
}

public class Cow : Animal
{
  public override string to_string()
  {
    return "I'm a cow!";
  }
}

public class Dog : Animal
{
  public override string to_string()
  {
    return "I'm a dog!";
  }
}

int main()
{
  Animal a;
  Type type;

  a = new Dog(); //< Явно создаем Dog, имя типа зарегистрировано в системе типов.
  print("1) %s\n", a.to_string());

  if((type = Type.from_name("Dog")) != Type.INVALID)
  {
    a = Object.new(type) as Animal;
    print("2) %s\n", a.to_string());
  }
  else
    print("2) Failed to get type!\n");

  if((type = Type.from_name("Cow")) != Type.INVALID)
  {
    a = Object.new(type) as Animal;
    print("3) %s\n", a.to_string());
  }
  else
  {
    print("3) Failed to get type! Trying type_from_name_lazy...\n");

    if((type = type_from_name_lazy("Cow")) != Type.INVALID)
    {
      a = Object.new(type) as Animal;
      print("3) %s\n", a.to_string());
    }
    else
      print("3) Failed to get type!\n");
  }

  return 0;
}

[CCode (has_target = false)]
delegate Type TypeGetFunc();

Type type_from_name_lazy(string name)
{
  void *func = null;

  if(Module.open(null, 0).symbol(ascii_camel_to_snake(name) + "_get_type", out func))
    return ((TypeGetFunc)func)();
  else
    return Type.INVALID;
}

string ascii_camel_to_snake(string input)
{
  var sb = new StringBuilder();

  for(int i = 0; input[i] != '\0'; i++)
  {
    if(
      ( input[i].isupper() && i > 0 && !input[i - 1].isupper() ) ||
      ( (i > 2 && input[i].isupper()) && input[i - 1].isupper() && input[i - 2].isupper() )
      )
      sb.append_c('_');

    sb.append_c(input[i].tolower());
  }

  return sb.str;
}


Вывод программы:
1) I'm a dog!
2) I'm a dog!
3) Failed to get type! Trying type_from_name_lazy...
3) I'm a cow!
И немного про Windows

Лично у меня данный пример как есть в Windows не заработал (собирал компилятором mingw). Не удавалось найти указатель на функцию cow_get_type. Опытным путем выяснилось, что работает такой механизм при соблюдении двух условий:
  1. Объект должен быть реализован в отдельной dll;
  2. До попытки поиска указателя должна быть вызвана хотя бы одна функция из этой dll.

В предыдущем посте «Клонирование GObject» я рассказывал про идею некоего обобщенного кода, не зависимого от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам. Предполагалось сохранять в текстовый файл имена классов и порядок объектов, а также имена свойств объектов и их значения.

Идея красивая, но здесь мы сталкиваемся с одним ограничением системы типов GObject (ограничением вполне естественным, т.к. написан GObjecct на Си и вся поддержка ООП реализована на уровне библиотеки): чтобы создать экземпляр класса по имени класса нужно, чтобы этот класс уже был зарегистрирован в системе типов GObject. Т.е. для типа FooBar должен быть заранее вызван метод foo_bar_get_type непосредственно или опосредованно (например, через метод создания вроде foo_bar_new).

Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type (см. тему на stack overflow) и вызова этого метода.

UPDATE: Найден довольно изящный и портабельный способ поиска метода foo_bar_get_type, см. Создание объектов GObject по имени типа. Часть 2.

Пример на Vala ниже иллюстрирует описанную проблему. Мы можем создать объект типа Dog по имени, т.к. выше уже создан объект типа Dog и класс зарегистрирован в системе типов. Создание объекта типа Cow же завершается неудачей (см. вывод программы после исходного кода).

public abstract class Animal : Object
{
  public abstract string to_string();
}

public class Cow : Animal
{
  public override string to_string()
  {
    return "I'm a cow!";
  }
}

public class Dog : Animal
{
  public override string to_string()
  {
    return "I'm a dog!";
  }
}

int main()
{
  Animal a;
  Type type;

  a = new Dog(); //< Явно создаем Dog, здесь класс и регистрируется в системе типов.
  print("1) %s\n", a.to_string());

  if((type = Type.from_name("Dog")) != 0)
  {
    a = Object.new(type) as Animal;
    print("2) %s\n", a.to_string());
  }
  else
    print("2) Failed to get type!\n");

  if((type = Type.from_name("Cow")) != 0)
  {
    a = Object.new(type) as Animal;
    print("3) %s\n", a.to_string());
  }
  else
    print("3) Failed to get type!\n");

  return 0;
}
Вывод программы:
1) I'm a dog!
2) I'm a dog!
3) Failed to get type!

Код основан на примере "Rotated button" из gtk-demo. Собирается под Gtk-2. Местами код неидеален (например виджет RotatedBin.scale создается вручную в main), но, думаю, вполне может быть полезен.

Скрин:
[Image]

Код:
https://docs.google.com/file/d/0B40UHa6ea9QkdUFTVnRqNnFaTDg/edit

Потребовалось использовать в программе на Vala некоторые функции небезызвестной библиотеки для вычисления быстрого преобразования Фурье fftw3. Для чего был написан простенький vapi-файл. В силу своего, прямо скажем, мизерного пока еще опыта использования Vala, не рекомендую брать данный vapi за эталон, тем не менее выкладываю его на случай, если кому-то он окажется полезен. В vapi прописаны лишь некоторые функции, типы и константы и лишь для работы с float-значениями, однако, думаю, при необходимости не составит никакого труда файл необходимым образом дополнить по аналогии. fftw_complex было принято решение не использовать, не заворачивать в класс, а по-простому работать напрямую с float-массивами (поэтому осторожней с выделением-освобождением памяти).

Сам vapi-файл:

[CCode (cprefix = "fftwf_", cheader_filename = "fftw3.h")]
namespace Fftwf
{
    [CCode(cprefix = "FFTW_")]
    enum Sign
    {
        FORWARD = -1,
        BACKWARD = 1
    }

    [CCode(cprefix = "FFTW_")]
    enum Flag
    {
        MEASURE         = (0U),
        DESTROY_INPUT   = (1U << 0),
        UNALIGNED       = (1U << 1),
        CONSERVE_MEMORY = (1U << 2),
        EXHAUSTIVE      = (1U << 3),
        PRESERVE_INPUT  = (1U << 4),
        PATIENT         = (1U << 5),
        ESTIMATE        = (1U << 6)
    }


    [CCode(cname = "fftwf_plan")]
    public struct Plan : int { } // fake int


    void    destroy_plan(Plan p);
    void    execute(Plan p);
    void    free(float *p);
    float*  malloc(size_t n);
    Plan    plan_dft_1d(int n, float* in_ptr, float* out_ptr, Sign sign, Flag flags);
}


Пример использования:

{
float input[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

float *data;
float *fft_result;
float *ifft_result;
Fftwf.Plan plan_forward, plan_backward;
int i;

data        = Fftwf.malloc( 2 * sizeof(float) * input.length );
fft_result  = Fftwf.malloc( 2 * sizeof(float) * input.length );
ifft_result = Fftwf.malloc( 2 * sizeof(float) * input.length );

plan_forward  = Fftwf.plan_dft_1d( input.length, (void*)data, (void*)fft_result, Fftwf.Sign.FORWARD, Fftwf.Flag.ESTIMATE );
plan_backward = Fftwf.plan_dft_1d( input.length, (void*)fft_result, (void*)ifft_result, Fftwf.Sign.BACKWARD, Fftwf.Flag.ESTIMATE );

/* populate input data */
for(i = 0 ; i < input.length ; i++)
{
    data[2 * i + 0] = input[i];
    data[2 * i + 1] = 0.0f;
}

/* print initial data */
for( i = 0 ; i < input.length ; i++ )
{
    print("data[%d] = { %2.2f, %2.2f }\n",
        i, data[2 * i + 0], data[2 * i + 1] );
}

Fftwf.execute( plan_forward );

/* print fft result */
for(i = 0 ; i < input.length ; i++)
{
    print("fft_result[%d] = { %2.2f, %2.2f }\n",
        i, fft_result[2 * i + 0], fft_result[2 * i + 1] );
}

Fftwf.execute( plan_backward );

/* print ifft result */
for( i = 0 ; i < input.length ; i++ )
{
    print("ifft_result[%d] = { %2.2f, %2.2f }\n",
        i, ifft_result[2 * i + 0] / input.length, ifft_result[2 * i + 1] / input.length );
}

/* free memory */
Fftwf.destroy_plan( plan_forward );
Fftwf.destroy_plan( plan_backward );

Fftwf.free(data);
Fftwf.free(fft_result);
Fftwf.free(ifft_result);
}

В заключение хочу порекомендовать отличное how-to по написанию vapi к не-GObject-библиотекам.

"Хозяйке на заметку": прочитать данные (переменные, целые, с плавающей точкой и т.п.) из бинарного файла на Vala можно и нужно с помощью GVariant, например так:


{
    uint8[] buf = new uint8[sizeof(uint32) + sizeof(uint32) +
        sizeof(double) + sizeof(double)];

    {
        var data_stream = new DataInputStream(file.read());
        data_stream.read(buf);
    }

    Variant variant = Variant.new_from_data<uint8>(
        new VariantType("(uudd)"), buf, true);

    uint32 int1 = variant.get_child_value(0).get_uint32();
    uint32 int2 = variant.get_child_value(1).get_uint32();
    double double1 = variant.get_child_value(2).get_double();
    double double2 = variant.get_child_value(3).get_double();

    stdout.printf("int 1 = %u\n", int1);
    stdout.printf("int 2 = %u\n", int2);
    stdout.printf("double 1 = %f\n", double1);
    stdout.printf("double 2 = %f\n", double2);
}



 

Copyright © 2007 DamnSmallBlog. Content is licensed under Creative Commons Attribution-Noncommercial.

Design: GeckoandFly and Blogcrowds.