Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2007.04.15;
Скачать: [xml.tar.bz2];

Вниз

Автоматическое уничтожение объектов   Найти похожие ветки 

 
jack128 ©   (2007-03-20 10:51) [0]

вот такая(наверно не новая) мысль.

например такой код.  
 MyObj1 := TMyObj1.Create;
 try
   while <> do
   begin
     MyObj2 := TMyObject2.Create;
     try
       ....
     finally
       MyObj2.Free;
     end;
   end;
 finally
   MyObj1.Free;
 end;

Бросается в глаза туча try-finally-end

Предлагается следать так

type
 IDestroyer = interface
 ["{2EAF5F09-7CAF-4799-8958-4DFB9D9C8ADD}"]
 end;

 TDestroyer = class(TInterfacedObject, IDestroyer)
 strict private
   FObj: TObject;
 public
   constructor Create(AObj: TObject);
   destructor Destroy; override;
 end;

function CreateDestroyerIntf(AObj: TObject): IDestroyer;
begin
 Result := TDestroyer.Create(AObj);
end;

{ TDestroyer }

constructor TDestroyer.Create(AObj: TObject);
begin
 FObj := AObj;
end;

destructor TDestroyer.Destroy;
begin
 FreeAndNil(FObj);
 inherited;
end;


Теперь исходный код можно переписать так:

 MyObj1 := TMyObj1.Create;
 CreateDestroyerIntf(MyObj1);
 while <> do
 begin
   MyObj2 := TMyObject2.Create;
   CreateDestroyerIntf(MyObj2);
   ...
 end;


На лицо сокращение кода. У кого есть какие соображения?


 
Ega23 ©   (2007-03-20 10:57) [1]


> На лицо сокращение кода. У кого есть какие соображения?


Я достаточно мало с интерфейсами работал. Не могу понять, а где, собственно, будет вызван destructor TDestroyer.Destroy?
При выходе из данной процедуры (Ну, из области видимости)?


 
jack128 ©   (2007-03-20 10:57) [2]

jack128 ©   (20.03.07 10:51)
MyObj1 := TMyObj1.Create;
CreateDestroyerIntf(MyObj1);
while <> do
begin
  MyObj2 := TMyObject2.Create;
  CreateDestroyerIntf(MyObj2);
  ...
end;

вместо многоточия естественно могут быть обращения к созданным обдъектам, иначе все это смысла бы не имело.

MyObj1 := TMyObj1.Create;
CreateDestroyerIntf(MyObj1);
while <> do
begin
  MyObj2 := TMyObject2.Create;
  CreateDestroyerIntf(MyObj2);
  MyObj1.DoWork();
  MyObj2.DoWork();
end;


 
Джо ©   (2007-03-20 10:57) [3]

> При выходе из данной процедуры (Ну, из области видимости)
> ?

Да.


 
Сергей М. ©   (2007-03-20 11:00) [4]


> jack128


Не проще ли сделать TMyObj1 и TMyObj2 наследниками TComponent и делать объект класса TMyObj1 владельцем всех объектов класса TMyObj2 ?


 
Джо ©   (2007-03-20 11:04) [5]

> [4] Сергей М. ©   (20.03.07 11:00)
>
> > jack128
>
>
> Не проще ли сделать TMyObj1 и TMyObj1  наследниками TComponent
> и делать объект класса TMyObj1 владельцем всех объектов
> класса TMyObj2 ?

Метод jack128 хорош тем, что не требует изменения классов TMyObj1 TMyObj2.


 
Ega23 ©   (2007-03-20 11:11) [6]


> Джо ©   (20.03.07 10:57) [3]
>
> > При выходе из данной процедуры (Ну, из области видимости)
> > ?
>
> Да.


Т.е., если я правильно понял, _Release всегда неявным образом вызовется при выходе из данной области видимости?
Век живи - век учись...


 
oxffff ©   (2007-03-20 11:13) [7]


> На лицо сокращение кода. У кого есть какие соображения?


В твоем подходе объекты, будут освобождаться при финализации временных переменных IDestroyer. Что не дает возможности отловить исключения.

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end
end;

У тебя все равно не будет контроля над освобождением объектов.

Да и зачем так сложно

IDestroyer вообще не нужен

TDestroyer = class(TInterfacedObject)
strict private
  FObj: TObject;
public
  constructor Create(AObj: TObject);
  destructor Destroy; override;
end;

function CreateDestroyerIntf(AObj: TObject): IInterface
begin
Result := TDestroyer.Create(AObj);
end;

Имхо. Теряется контроль над освобождением


 
SlymRO ©   (2007-03-20 11:16) [8]

jack128 ©   (20.03.07 10:51)
Из Delphi Вижулбейсик делаешь?


 
oxffff ©   (2007-03-20 11:16) [9]

Если при освобождении произойдет exception, то другие объекты не будут освобождены.

destructor TDestroyer.Destroy;
begin
FreeAndNil(FObj);
end;

Если модифицировать так

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end;
end;

Лучше. Но контроля не будет.


 
wicked ©   (2007-03-20 11:25) [10]

кстати, исходный код при некоторых условиях можно было бы и переделать
так и просится вынести создание и удаление MyObj2 за цикл while, если он не строго завязан на счетчик цикла
тогда и не было бы этих вложенных try-finally

или, как вариант, оставить все как есть, только забрать внутренние try-finally, а во внешнем блоке finally проверять, удален ли MyObj2
например
MyObj1 := TMyObj1.Create;
try
  while <> do
  begin
    MyObj2 := TMyObject2.Create;
      ....
    FreeAndNil(MyObj2);
  end;
finally
  if Assigned(MyObj2) then
     MyObj2.Free;
  MyObj1.Free;

end;


и оффтопик
вот как надо извращаться, когда нету автоматических обьектов... :-)
например, ПОЛНОСТЬЮ аналогичный код на билдере
auto_pointer<MyObj1> = new TMyObj1();
 while(<>){
    auto_pointer<MyObj2> = new TMyObject2();
      ....
 }

auto_pointer - либо boost::shared_ptr (в написании могу ошибаться)
либо что нибудь похожее, smart pointer на строчек 100 кода (у меня свой, доморощенный)


 
Аноним   (2007-03-20 11:26) [11]


> destructor TDestroyer.Destroy;
> begin
> try
> FreeAndNil(FObj);
> except
> end;


Расстреливать.


 
oxffff ©   (2007-03-20 11:32) [12]


> Аноним   (20.03.07 11:26) [11]
>
> > destructor TDestroyer.Destroy;
> > begin
> > try
> > FreeAndNil(FObj);
> > except
> > end;
>
>
> Расстреливать.


Нужно аргументировать свою точку зрения.

А теперь по списку.

Посмотри реализацию _FinalizeArray.

Поэтому обертка поможешь, гарантированно освободить "все"  объекты в _FinalizeArray.

Поэтому думать нужно.
А потом делать такие громкие высказывания.


 
jack128 ©   (2007-03-20 11:37) [13]

wicked ©   (20.03.07 11:25) [10]
auto_pointer<MyObj1> = new TMyObj1();
while(<>){
   auto_pointer<MyObj2> = new TMyObject2();
     ....
}

Хм. А зачем вообще такой гемор, если можно просто написать:
TMyObj1 MyObj1;
?
oxffff ©   (20.03.07 11:13) [7]
исключения в деструкторах объектов практически невозможно коректно обработать. На это мона забить.


 
oxffff ©   (2007-03-20 11:38) [14]


> или, как вариант, оставить все как есть, только забрать
> внутренние try-finally, а во внешнем блоке finally проверять,
>  удален ли MyObj2
> например
> MyObj1 := TMyObj1.Create;
> try
>   while <> do
>   begin
>     MyObj2 := TMyObject2.Create;
>       ....
>     FreeAndNil(MyObj2);
>   end;
> finally
>   if Assigned(MyObj2) then
>      MyObj2.Free;
>   MyObj1.Free;
>
> end;


Если MyObj2.Free сгенерирует исключение, то ты благополучно покинешь
try finally и MyObj1.Free не будет и освобожден.


 
jack128 ©   (2007-03-20 11:39) [15]

Сергей М. ©   (20.03.07 11:00) [4]
Не проще ли сделать TMyObj1 и TMyObj2 наследниками TComponent и делать объект класса TMyObj1 владельцем всех объектов класса TMyObj2


А с TList/TStream и тд что делать?


 
wicked ©   (2007-03-20 11:42) [16]

> jack128 ©   (20.03.07 11:37) [13]

> Хм. А зачем вообще такой гемор, если можно просто написать:
>
> TMyObj1 MyObj1;

можно, не учел...
но в билдере есть "два вида классов" (цы) ИШ, и для унаследованных от TObject нужно делать именно через указатели, new и delete


 
Сергей М. ©   (2007-03-20 11:45) [17]


> jack128 ©   (20.03.07 11:39) [15]


Почему бы сразу не предупредить, что речь идет и о "генофонде" в том числе ?)


 
oxffff ©   (2007-03-20 11:46) [18]


> oxffff ©   (20.03.07 11:13) [7]
> исключения в деструкторах объектов практически невозможно
> коректно обработать. На это мона забить.


oxffff ©   (20.03.07 11:16) [9]
oxffff ©   (20.03.07 11:32) [12]

Либо переопределить _FinalizeArray о чем просят в qc.
Очент маловероятно, что они на это пойдут.
Но даже так много не добъешься.


 
Аноним   (2007-03-20 11:48) [19]


> Поэтому думать нужно.
> А потом делать такие громкие высказывания.


Думаем. Осознаем разницу между отсутствием ошибки и ее сокрытием.
PS. В деструкторах штатных исключений быть не должно. Такого мое категорическое мнение. Если там появляются исключения, программу нужно править. А для начала об этом исключении неплохо бы узнать.


 
oxffff ©   (2007-03-20 11:56) [20]


> Аноним   (20.03.07 11:48) [19]
>
> > Поэтому думать нужно.
> > А потом делать такие громкие высказывания.
>
>
> Думаем. Осознаем разницу между отсутствием ошибки и ее сокрытием.
>
> PS. В деструкторах штатных исключений быть не должно. Такого
> мое категорическое мнение. Если там появляются исключения,
>  программу нужно править. А для начала об этом исключении
> неплохо бы узнать.


Моя модификация вызвана тем, чтобы дать возможность другим финализируемым финализироваться в _FinalizeArray, при возникновении исключении в деструкторе финализируемого.

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end;

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

Мы люди взрослые, поэтому выбирайте выражения.


 
jack128 ©   (2007-03-20 11:57) [21]

oxffff ©   (20.03.07 11:46) [18]
Но даже так много не добъешься.

ну методом copy-paste мона наплодить тучу таких структур :-)
TStringsRec = record
private
 FStrings: TStrings;
public
 contructor Create(AStrings: TString);
 destructor Destroy;
 class operator Implicit(const A: TStringsRec): TStrings;
end;


и юзать
var
 Strings: TStrings;
begin
 Strings := TStringsRec.Create(TStringList.Create);
 ...
end;

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


 
oxffff ©   (2007-03-20 12:01) [22]


> jack128 ©   (20.03.07 11:57) [21]
> oxffff ©   (20.03.07 11:46) [18]
> Но даже так много не добъешься.
> ну методом copy-paste мона наплодить тучу таких структур
> :-)
> TStringsRec = record
> private
>  FStrings: TStrings;
> public
>  contructor Create(AStrings: TString);
>  destructor Destroy;
>  class operator Implicit(const A: TStringsRec): TStrings;
>
> end;
>
> и юзать
> var
>  Strings: TStrings;
> begin
>  Strings := TStringsRec.Create(TStringList.Create);
>  ...
> end;
> Тут, так же как и с интерфейсами закладка идет на то, что
> записи будут уничтожаться именно при завершении процедуры.
>
> Так что извратов всяких мона придумать...


Не сработает. Нельзя определить деструктор для записи.


 
jack128 ©   (2007-03-20 12:03) [23]

oxffff ©   (20.03.07 12:01) [22]
Не сработает. Нельзя определить деструктор для записи.


Ты не понял. Это для случая, если коджир разрешит переопределять _FinallizeRecord.


 
Аноним   (2007-03-20 12:03) [24]


> oxffff ©  


> Связана с желанием хотя бы "улучшить" состояние с памятью


А зачем улучшать "состояние с памятью" в случае заведомого присутствия ошибки в программе? Ошибку надо править, и тогда состояние памяти само улучшится


> гарантировать отсутствие исключений не может


Не может. Поэтому желает о них узнавать. Что хуже - получить мем лик в режиме тестирования, или пропустить незамеченный баг в релиз?


> даже вы, умный вы наш.


Спасибо.


> Мы люди взрослые, поэтому выбирайте выражения.


ОК


 
jack128 ©   (2007-03-20 12:09) [25]

Так, обсуждение не туда скатывается.  По поводу _исходной_ реализации с интерфейсами - есть у кого какие соображения? В плане -
"Нет, это полный ацтой, потому то-потому то"  
   Или наоборот,
"Это рулезная идея, а то что, тот гаврик сказал, мол все плохо, так он - ламо потому-то, потому то"
:-)


 
oxffff ©   (2007-03-20 12:14) [26]


> Не может. Поэтому желает о них узнавать. Что хуже - получить
> мем лик в режиме тестирования, или пропустить незамеченный
> баг в релиз?


Я бы очень хотел с вами согласиться. Но не могу. Опыт не позволяет.

Программ без ошибок нет и не будет в ближайшее время.  

Так вы хотя бы позволите пользователю дорабоать с программой хотя бы в "полуштатном режиме".

А если следовать вашей точки зрения, то тогда зачем в релиз версии
зачем эти манипуляции с fs:[0] (try except, try finally)?

Ну не может все работать на все 100%. Дай бог, чтобы на 99.999%.
А что делать с одной сотой?
Похоже на errata в процессоре. Но никто не изымает процессоры.


 
oxffff ©   (2007-03-20 12:23) [27]


> jack128 ©   (20.03.07 12:09) [25]
> Так, обсуждение не туда скатывается.  По поводу _исходной_
> реализации с интерфейсами - есть у кого какие соображения?
>  В плане -
> "Нет, это полный ацтой, потому то-потому то"  
>    Или наоборот,
> "Это рулезная идея, а то что, тот гаврик сказал, мол все
> плохо, так он - ламо потому-то, потому то"
> :-)


Взгляд со стороны

Разница в коде?

1. MyObj2:=TObject.create;
  CreateDestroyerIntf(MyObj2);
  .......

2. MyObj2:=TObject.create;
  ......
  MyObj2.free;


 
Джо ©   (2007-03-20 12:27) [28]

> Разница в коде?

Разница не в этом, а в возможности избегать (вложенных) try/finally.


 
oxffff ©   (2007-03-20 12:35) [29]


> Разница не в этом, а в возможности избегать (вложенных)
> try/finally.


Аргументирую свою точку зрения.

_FinallizeRecord не содержит try  блока

Даже если мы модифицируем

destructor TDestroyer.Destroy;
begin
FreeAndNil(FObj);
inherited;
end;

на

destructor TDestroyer.Destroy;
begin
try
FreeAndNil(FObj);
except
end
end;

Это не дает гарантии, что все финализуремые финализируются.
Могут быть другие интерфейсы, record, variants,string,DynamicArrays..
Которые возбудят исключение, а мы благополучно покинем _FinallizeRecord.

Объясните свою точку зрения.


 
oxffff ©   (2007-03-20 12:37) [30]


> благополучно покинем _FinallizeRecord.


Читать как "благополучно"  или неблагополучно


 
Ega23 ©   (2007-03-20 12:39) [31]

Всё-таки не совсем ясно, из-за чего весь сыр-бор.
Честно говоря, я мало где встречал конструкции типа

obj1 := TMyObj.Create;
try
 obj2 := TMyObj.Create;
 try
   obj3 := TMyObj.Create;
   try
      ...........
   finally
     obj3.Free;
   end;  
 finally
   obj2.Free;
 end;
finally
 obj1.Free;
end;


Да, с академической точки зрения такой код наиболее правильный (а то фиг его знает, чё там в конструкторе TMyObj.Create).
С другой стороны - ну максимум 2 вложенных try..finally + один try..except
Это лично у меня и то крайне редко. В VCL тоже подомных монстров не сильно замечал. Да и обкладывать всё на свете блоком try..except - ИМХО чистая паранойя. На небезопасных участках - да, безусловно.

ИМХО, сама по себе идея - ничё так. Но лично я бы не стал использовать. Мне проще и нагляднее try..finally написать.


 
Джо ©   (2007-03-20 12:43) [32]

> [29] oxffff ©   (20.03.07 12:35)
> Объясните свою точку зрения.

Я, наверное, не очень ее понял.

Вот код:

O1 := TObject.Create;
CreateDestroyerIntf(O1);
O1.SomeMethod; // допустим, тут будет исключение

O2 := TObject.Create;
CreateDestroyerIntf(O2);


Деструктор для O1 вызовется. Для O2, разумеется, не вызовется. Но он и не нужен, ибо O2 := TObject.Create ведь тоже не вызовется :)


 
Джо ©   (2007-03-20 12:44) [33]

> [31] Ega23 ©   (20.03.07 12:39)
> ИМХО, сама по себе идея - ничё так. Но лично я бы не стал
> использовать. Мне проще и нагляднее try..finally написать.

Придерживаюсь того же мнения :)


 
oxffff ©   (2007-03-20 12:53) [34]


> Джо ©   (20.03.07 12:43) [32]
> > [29] oxffff ©   (20.03.07 12:35)
> > Объясните свою точку зрения.
>
> Я, наверное, не очень ее понял.
>
> Вот код:
>
> O1 := TObject.Create;
> CreateDestroyerIntf(O1);
> O1.SomeMethod; // допустим, тут будет исключение
>
> O2 := TObject.Create;
> CreateDestroyerIntf(O2);
>
>
> Деструктор для O1 вызовется. Для O2, разумеется, не вызовется.
>  Но он и не нужен, ибо O2 := TObject.Create ведь тоже не
> вызовется :)


А c чего Деструктор для O1 вызовется?
Мы ведь покинем процедуру при O1.SomeMethod;
и до финализатора не доберемся

?

Или вы хотите модифицировать до

try

O1 := TObject.Create;
CreateDestroyerIntf(O1);
O1.SomeMethod; // допустим, тут будет исключение
O2 := TObject.Create;
CreateDestroyerIntf(O2);
except
end;

Но при

  finally
    obj3.Free;
  end;  
finally
  obj2.Free;
end;

obj2.Free выполниться при любом исходе выполнения obj3.Free

А Финализатор, такой гарантии не дает


 
Аноним   (2007-03-20 12:58) [35]


> Я бы очень хотел с вами согласиться. Но не могу. Опыт не
> позволяет.
>
> Программ без ошибок нет и не будет в ближайшее время.  
>
> Так вы хотя бы позволите пользователю дорабоать с программой
> хотя бы в "полуштатном режиме".
>
> А если следовать вашей точки зрения, то тогда зачем в релиз
> версии
> зачем эти манипуляции с fs:[0] (try except, try finally)?
>


А мне опыт как раз позволяет. Поясню:
Появление мем лика в полуштатном режиме при работе пользователя - это неприятность, но не такая большая, кроме того она проявится в заведомо ошибочной ситуации в программе, причем исключение  будет обусловлено именно ошибкой в программе.
Что касается

> А если следовать вашей точки зрения, то тогда зачем в релиз
> версии
> зачем эти манипуляции с fs:[0] (try except, try finally)?
>


Потому что ошибка может быть еще и в данных, что вписывается в понятие "штатный режим эксплуатации". А ошибка в коде в это понятие не вписывается.

Предположим вам надо в процедуре создать 5 экземпляров разных классов, и по окончании процедуры их разрушить
я имею в виду не в цикле, а типа

Obj1:=TObject1.Create;
Obj2:=TObject2.Create;
Obj3:=TObject3.Create;
Obj4:=TObject4.Create;
Obj5:=TObject5.Create;

Если заботиться о "улучшении памяти" по вашей концепции, то надо делать 5 вложенных блоков try-finally

Только честно - делаете? Везде?


 
Джо ©   (2007-03-20 13:03) [36]

[34] oxffff ©  
> А c чего Деструктор для O1 вызовется?
> Мы ведь покинем процедуру при O1.SomeMethod;

IntfClear вызовется до того, как мы «покинем процедуру». Вот вы попробуйте, попробуйте :)

П.С. Простой тест:

TMyObject = class (TObject)
public
  destructor Destroy; override;
end;

...

{ TMyObject }

destructor TMyObject.Destroy;
begin
 ShowMessage ("Destroyed")
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 O1: TMyObject;
begin
 O1 := TMyObject.Create;
 CreateDestroyerIntf(O1);
 raise Exception.Create("Exception raised");
end;



 
oxffff ©   (2007-03-20 13:15) [37]


> А мне опыт как раз позволяет. Поясню:
> Появление мем лика в полуштатном режиме при работе пользователя
> - это неприятность, но не такая большая, кроме того она
> проявится в заведомо ошибочной ситуации в программе, причем
> исключение  будет обусловлено именно ошибкой в программе.
>
> Что касается


"заведомо ошибочной ситуации в программе"?
Вы всегда можете указать на это место?
Даже после тестирования, ошибки остаются.

И наличие механизмов обеспечивающих robust при нештатных ситуациях является упреждающим шагом.

>Потому что ошибка может быть еще и в данных

Объект должен проверять данные, чтобы это не приводило к его краху.
А если он этого не делает, тогда это ошибка в коде. IMHO

>Только честно - делаете? Везде?

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


 
oxffff ©   (2007-03-20 13:23) [38]


> Джо ©   (20.03.07 13:03) [36]
> [34] oxffff ©  
> > А c чего Деструктор для O1 вызовется?
> > Мы ведь покинем процедуру при O1.SomeMethod;
>
> IntfClear вызовется до того, как мы «покинем процедуру».
>  Вот вы попробуйте, попробуйте :)


О.
Я действительно не знал этого. Огромное спасибо за приобретенный skill


 
oxffff ©   (2007-03-20 13:36) [39]


> О.
> Я действительно не знал этого. Огромное спасибо за приобретенный
> skill


Да никогда не обращал внимание на код
после jmp @HandleFinally
а там он самый jmp -18 на финализатор.

Век живи, век учись. Спасибо Джо.


 
Джо ©   (2007-03-20 13:37) [40]

> [39] oxffff ©   (20.03.07 13:36)
> Век живи, век учись.

Дык, на то оно и автоматическое уничтожение интерфейсов в Делфи :)



Страницы: 1 2 вся ветка

Форум: "Прочее";
Текущий архив: 2007.04.15;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.064 c
2-1174903322
Darvin
2007-03-26 14:02
2007.04.15
TPrintDialog + отсутствие принтеров в системе


15-1174651657
Bless
2007-03-23 15:07
2007.04.15
А у нас пыльная буря!


2-1174745845
Михаил Н
2007-03-24 17:17
2007.04.15
Работа с датой


11-1143026595
Unknown Mystic
2006-03-22 14:23
2007.04.15
Задание StateImage для эл-тов TKOLTreeView при создании формы.


15-1174209791
Redwwq
2007-03-18 12:23
2007.04.15
Компьютер зависает





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский