Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет qwerty ([info]qwerty)
@ 2008-03-25 12:12:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Симуляция жабьими средствами множественного наследования и передачи параметров по ссылке

Выпало компилять в Жабу язычок с множественным наследованием, передачей пароаметров по ссылке и значению, сборкой мусора и обильными библиотеками. Открыто настоящим именем называть язычок не велят, видимо, чтобы не пришел, да и какая разница. Жаба - не очень-то подходящий целевой язык. С одной стороны, все возможности исходного языка прямо ее средствами не выражаются. С другой, низкоуровневый машинно-независимый ассемблер из нее тоже не ахти - адресов и смещений почти нет, равно как и контроля за размещением в памяти.

1. Каким путем мы не пойдем
Можно было бы, конечно, взять в руки кувалду побольше отвести жабьми средствами массив байтов и дальше все внутри организовать вручную и пользоваться индексами вместо адресов. Но так я делал не раз, потому мне не интересно, к тому же у способа есть недостатки - пришлось бы для этого массива писать свой сборщик мусора, да и трудно пользовать многочисленные жабьи библиотеки.

2. Товарищи по ссылке
В Жабе у локалов адресов нет, смещений нет, и запись активации не объект. Соответственно, простыми средствами передачу параметров по ссылке не организовать. При этом семантика передачи параметров по значению в язычке в целом соответствует жабьей - есть элементарные типы, которые копируются, и объекты, которые нет. Элементарыне типы тоже примерно одинаковые. Для скорости и пущей розрачности хочется передачу параметра по значению оставить передачей по значению и мучаться лишь с передачей по ссылке.

Поступаем так. Для каждого элементарного типа исходного язычка type и бестипового указателя на объект заводим жабий класс Variable$typeдля хранения переменной этого типа. В классе описываем аксессоры и все необходимые операции над переменной соответствующего типа.

Далее, если точно известно, что переменная исходного языка нигде не передается по ссылке, транслируем ее непосредственно в элементарный жабий тип. Собственно, такая уверенность есть только по отношению к локалам и приватным полям финальных классов. Во всех прочих случаях переменную транслируем в объект класса Variable$type. При передаче переменной по ссылке передаем адрес этого объекта. При передаче по значению извлекаем значение из объекта и передаем его.

Ужас, да. Но не ужас-ужас-ужас. Ужас и моральный террор впереди.

3. Множественное наследование
В жабе есть множественное наследование среди интерфейсов. Среди классов его нет. Нет возможности вставить один объект внутрь другого. По непонятной причине в интерфейсах нельзя писать кода. Чуть более понятно, почему в них нельзя писать деклараций полей. Кряхтя и плюясь, поступаем так.

Отделяем тип от представления. Тип моделируем жабьим интерфейсом. В для публичных переменных в интерфейс принудительно добавляем аксессоры к полям, представленными объектами-переменными Variable$type. Представление моделируем классом без наследования. Наследованные поля вручную копируем из представлений надклассов язычка, переименовывая по необходимости (привет процессу сочленения из Симулы-67).

Осталось организовать наследование методов. Для связывания с интерфейсом метод должен быть виртуальным. Однако ж наследования на уровне представлений нет, поэтому, чтобы избежать копирования тела, тело заключаем в статическую функцию.По мере необходимости добавляем явный параметр для объекта-получателя this/self и параметры для всех используемых полей, чтобы отвязать значения от мест их хранения.

Вот, собственно, и все. Теперь страшный пример для зануд, дочитавших до этого места.

Исходный текст на язычке (синтаксис изменен):

class A {
 public:
  int a;
  int& get_ref_a() { return &a; }
  int  get_a()     { return a; }
  void f()         { get_ref_a()++; }
};

class B {
 private:
  int b;
 public:
  int& get_ref_b() { return &b; }
};

class C: A, B {
 private:
  int c;
 public:
  int& get_ref_a() { return get_ref_b(); }
  int h() { return A::a + get_ref_a() + c; }
};


Результат трансляции (подлежит улучшению путем выбрасывания лишнего):
interface Type$A {
 public:
  Variable$int a();
  Variable$int get_ref_a();
  int get_a();
  void f();
};

class Representation$A implements Type$A {
 private:
  Variable$int var$a;
 public:
  Variable$int a() { return var$a; }

  // Тело метода для ссылки при наследовании
  // Все используемые поля передаются параметрами
  static Variable$int get_ref_a(Variable$int a) {
    return var$a;
  }
  // Связывание с интерфейсом
  Variable$int get_ref_a() {
    return get_ref_a(var$a);
  }

  // Тело метода для ссылки при наследовании
  // Все используемые поля передаются параметрами
  static int get_a(int a) {
    return a;
  }
  // Связывание с интерфейсом
  int get_a() {
    return get_a(var$a.get());
  }

  // Тело метода для ссылки при наследовании
  // Все используемые поля передаются параметрами
  // Причем именно Type, а не Representation
  static void f(Type$A self) {
    return self.get_ref_a().inc();
  }  
  // Связывание с интерфейсом
  void f() { f(this); }
};


interface Type$B {
 public:
  Variable$int get_ref_b();
};

class Representation$B implements Type$B {
 private:
  Variable$int var$b;
 public:
  static Variable$int get_ref_b(Variable$int b) {
    return b;
  }
  // Связывание с интерфейсом
  Variable$int get_ref_b() {
    return get_ref_b(b);
  }
};


interface Type$С extends Type$A, Type$B {
 private:
  int c;
 public:
  int h();
};

class Representation$C implements Type$C {
 private:
  Variable$int var$A$a;
  Variable$int var$B$b;
  Variable$int var$c;

 public:
  // Привязываем наследованное статическое тело к полям
  Variable$int a() { return Representation$A.a(var$A$a); }

  // Тело метода для ссылки при наследовании
  // Все используемые поля передаются параметрами
  static Variable$int get_ref_a(Variable$int b) {
    return Representation$B::get_ref_b(b);
  }
  // Наследованный метод перекрыт
  Variable$int get_ref_a() {
    return get_ref_a(var$B$b);
  }

  // Привязываем наследованное статическое тело к полям
  int get_a() { return Representation$A.get_a(var$A$a.get()); }

  // Привязываем наследованное статическое тело к полям
  void f() { Representation$A.f(this); }

  // Привязываем наследованное статическое тело к полям
  Variable$int get_ref_b() { return Representation$B.get_ref_b(var$B$b); }

  // Тело метода для ссылки при наследовании
  // Все используемые поля передаются параметрами
  static int h( Type$С self, int a, int с ) {
    return a + self.get_ref_a() + c;
  }
  // Связывание с интерфейсом
  int h() {
    return h( this, A$a.get(), c.get() );
  }
};


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


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


(Анонимно)
2008-03-26 11:01 (ссылка)
Чо, очередной аспирант-в универе-пола подметатель - энергии много, с мозгами - напряг?
В джаве множественного наследования нет потому, что оно нах не нужно, а если очень хочется, делается через aggregation и method forwarding, в не через жопу.
Касательно передачи "параметров по ссылке". Они, прикинь, именно по ссылке и передаются. Хочется передать по ссылке "plain old types" - используй wrappers . Хочется возвращать созданные внутри метода много объектов - засунь их в List или Map и верни. Понаписал хуйни, блин.

(Ответить) (Ветвь дискуссии)


[info]qwerty
2008-03-26 11:06 (ссылка)
Весеннее обострение?

(Ответить) (Уровень выше)


[info]blue_slonopotam
2008-03-27 01:41 (ссылка)
>Результат трансляции
ручной, поскольку опечатки, правильно ?

>чтобы избежать копирования тела
А чему это мешало ?

>подлежит улучшению путем выбрасывания лишнего
Что имелось ввиду ?

(Ответить) (Ветвь дискуссии)


[info]qwerty
2008-03-27 01:52 (ссылка)
Угу. Могу автоматическую, но там все выглядит не ахти.

Копирование тела еще более увеличило бы размер, что не есть хорошо - потом получившуюся калябру грузить из инета.

Генерируется много оберточных методов, большинство которых никогда не используется, но во время трансляции отдельно взятого класса нельзя быть в том уверенным. Они примерно играют роль тривиальных инлайновых функций для перенаправления методов из сынка в папеньку.

(Ответить) (Уровень выше)