Реализация на C++ аналогов операторов is и as из C#

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
srg320
Встал на лапы
Сообщения: 85
Зарегистрирован: Пт фев 01, 2013 17:47:26
Откуда: Украина, Луганская область

Реализация на C++ аналогов операторов is и as из C#

Сообщение srg320 »

Для начала предыстория.
Был у меня удачный опыт запуска GUI microwindows в связке с RTOS TNKernel на STM32F4 и дисплее от Samsung S5233. Всё вроде бы хорошо: рисует на экране окна и контролы, и всё это работает с тачскрином, но сделать богатый интерфейс на WinAPI очень сложно (по крайней мере для меня). Поэтому я решил пойти по пути Microsoft и написать оболочку над WinAPI по типу Windows Forms в .Net Framework. Ну, написать это сильно сказано, я просто беру исходники .Net Framework и тупо переделываю их с C# на C++. Сделал базовый класс Object, и классы Control, Form, Button, CheckBox, RadioButton и др. Всё это замечательно работало, пока не приступил к реализации меню MenuStrip из Windows Forms, и столкнулся с операторами языка C# “is” и ”as”, которых естественно в С++ нет.
Начнём с оператора проверки типа “is”. Использую компилятор IAR, в котором вроде бы есть RTTI, думал использовать typeid и type_info, но, то ли IAR их не поддерживает, то ли у меня ума не хватило использовать их, я решил пойти по другому пути. В базовом классе Object добавил член класа int id, в который заносится идентификатор в конструкторе класса, и метод bool Is(int id), который как раз и реализует оператор “is”:
Спойлер

Код: Выделить всё

namespace System {

class Type
{
public:    
    const static int TypeObject = 0;
    const static int TypeString = 1;
    const static int TypeControl = 2;
    const static int TypeScrollableControl = 8;
    
    const static int TypeForm = 3;
    const static int TypeButtonBase = 4;
    const static int TypeButton = 5;
    const static int TypeCheckBox = 6;
    const static int TypeLabel = 7;
    const static int TypeRadioButton = 9;
    const static int TypeListBox = 10;
    const static int TypeComboBox = 11;
    const static int TypeTextBoxBase = 12;
    const static int TypeTextBox = 13;
    const static int TypeMainMenu = 14;
    const static int TypeMenuItem = 15;
    const static int ArrangedElementCollection = 16;
    const static int LayoutEngine = 17;
    const static int EventArgs = 18;
    const static int IArrangedElement = 19;
    const static int FlowLayout = 20;
    const static int DefaultLayout = 21;
    const static int ControlEventArgs = 22;
    const static int IMessageFilter = 23;
    const static int IMessageModifyAndFilter = 24;
       
    const static int ToolStripItemCollection = 200;
    const static int ToolStripSplitStackLayout = 201;
    const static int ToolStrip = 202;
    const static int ToolStripItem = 203;
    const static int ToolStripDropDownItem = 204;
    const static int ToolStripMenuItem = 205;
    const static int MenuStrip = 206;
    const static int ToolStripItemRenderEventArgs = 207;
    const static int ToolStripItemTextRenderEventArgs = 208;
    const static int ToolStripRenderer = 209;
    const static int ToolStripSystemRenderer = 210;
    const static int ToolStripDropDown = 211;
    const static int ToolStripLayoutData = 212;
    const static int ToolStripItemLayoutOptions = 213;
    const static int ToolStripItemInternalLayout = 214;
    const static int ToolStripDropDownMenu = 215;
    const static int ToolStripDropDownLayoutEngine = 216;
    const static int ModalMenuFilter = 217;
};

class String;
class Object
{
public: 
    int id;
        
    Object():
        id(Type::TypeObject)
    { }
        
    bool Is(int id)
    {
        return id == this->id;
    }
    
    virtual String ToString();
};

class String : public Object
{
public:
    String() 
    {id = Type::TypeString;}
.
.
.
};

};

namespace System { namespace Windows{ namespace Forms{

class Control : public virtual Object, public IArrangedElement
{
public:
    Control()
    {id = Type::TypeControl;}
.
.
.
};
class ScrollableControl : public Control
{
.
.
.
};

class Form : public ScrollableControl
{
public:
    Form ()
    {id = Type::TypeForm;}

.
.
.
};

};};};
Так всё работало, пока дерево классов не начало разрастаться, и вот такой проверочный код уже работал не правильно:
Спойлер

Код: Выделить всё

bool res;
Object* o = new Object();
Control* c = new Control();
Form* f = new Form();
res = o->Is(Type::Object);		//true
res = o->Is(Type::Control);		//false
res = o->Is(Type::Form);		//false

res = c->Is(Type::Object);		//false - ошибка
res = c->Is(Type::Control);		//true
res = c->Is(Type::Form);		//false

res = f->Is(Type::Object);		//false - ошибка
res = f->Is(Type::Control);		//false - ошибка
res = f->Is(Type::Form); 		//true
Тогда я решил пойти другим путём:
Спойлер

Код: Выделить всё

namespace System {

class Type
{
public:
    Type* BaseType;
    Type(Type* baseType=0) :
		BaseType(baseType)
    {
        
    }

    static Type* Object;
    static Type* Control;
    static Type* Form;   
};

Type* Type::Object = new Type();
Type* Type::Control = new Type(Type::Object);
Type* Type::Form = new Type(Type::Control);

class Object
{
public:
    Type* type;
	Object()
	{ type = Type::Object; }

	bool Is(Type* type)
	{
		Type* t = this->type;
		while (t)
		{
			if (type == t) return true;
			t = t->BaseType;
		}
		return false;
	}
};

};

namespace System { namespace Windows{ namespace Forms{

class Control : public Object
{
public:
	Control()
	{ type = Type::Control; }
.
.
.
};

class Form : public Control
{
public:
	Form()
	{ type = Type::Form; }
.
.
.
};
};};};
Теперь работает как надо
Спойлер

Код: Выделить всё

bool res;
Object* o = new Object();
Control* c = new Control();
Form* f = new Form();
res = o->Is(Type::Object);		//true
res = o->Is(Type::Control);		//false
res = o->Is(Type::Form);		//false

res = c->Is(Type::Object);		//true
res = c->Is(Type::Control);		//true
res = c->Is(Type::Form);		//false

res = f->Is(Type::Object);		//true
res = f->Is(Type::Control);		//true
res = f->Is(Type::Form); 		//true
Но чтобы это работало, нужно при добавлении нового класса, не забывать, во-первых создать новый объект Type для него, во-вторых, в конструкторе класса присваивать этот объект полю type.


А теперь собственно вопрос. Можно ли реализовать аналог оператора “is” из .Net на С++ каким ни будь другим способом, или автоматизировать мой вариант, чтобы не нужно было вручную прописывать каждый объект Type для каждого нового класса, а ведь их будет в будущем дофига.
Второй вопрос: реализация аналога оператора “as”, я даже не представляю как это сделать. Простое приведение типов не подходит потому как, например, следующие строки будут срабатывать не правильно:
Спойлер

Код: Выделить всё

Object* obj = new Object();
Control* c = new Control();
Form* f = new Form();

Control* temp;

//C# temp = obj as Control; 	//тут temp должен быть равен 0
temp = (Control*) obj;		//а он равен obj 

//C# temp = f as Control; 		//а тут temp равен f
temp = (Control*)f;			//здесь верно
Так как, опыта в программировании на C++ у меня ещё мало, прошу помощи в данных вопросах, возможно кто-то встречал где-то подобные реализации.
Заранее благодарен.
Реклама
Аватара пользователя
blackx
Говорящий с текстолитом
Сообщения: 1518
Зарегистрирован: Пт дек 28, 2012 21:56:46
Откуда: St. Petersburg

Re: Реализация на C++ аналогов операторов is и as из C#

Сообщение blackx »

Не разбираюсь ни в том ни в другом (плюсы просто уже забыл). Просто мысли.
Microsoft писал(а): An is expression evaluates to true if both of the following conditions are met:
expression is not null.
expression can be cast to type. That is, a cast expression of the form (type)(expression) will complete without throwing an exception. For more information, see 7.6.6 Cast expressions.
Педивикия писал(а): В языке программирования C++ оператор dynamic_cast является частью механизма динамической идентификации типа данных, который позволяет выполнять приведение типа данных. В отличие от обычного приведения типа в стиле Си, проверка корректности приведения типов производится во время выполнения программы. Оператор dynamic_cast может быть применён к указателям или ссылкам. В случае если осуществляется преобразование указателя к типу данных, который не является фактическим типом объекта, в результате преобразования будет получен нулевой указатель.

Код: Выделить всё

#define _IS_(obj, cls) \
(dynamic_cast<cls *>(&obj) != NULL)

// usage:

class A;
void main(){
A a;
if(_IS_(a, A)) {
std::cout<<"Fuck, it works!" << std::endl;
}

}


Не исключаю что это может быть полный бред, вызванный моей многолетней деградацией от Си и Ассемблера.

UPD. Что-то меня понесло:

Код: Выделить всё

// после такого - не гарантирую ничего :D
#define is %


class Object{

int operator%(const void *obj) {
return (dynamic_cast<cls *>(&obj) != NULL);
}

};

class A:Object{};

void main(){
A a;
if(a is A) {
std::cout<<"Fuck, it works! O_O O_O O_O O_O" << std::endl;
}

}

Тут ненужный нигде никогда и никому оператор % заменяется на is. Скорее всего не заработает :)))

В общем, отпусти меня, чудо-трава! :))) :))) :)))

А если серьезно, то уверен, что решение найдется. Но вы лучше бы спросили на форуме по C++, тут плюсы не очень популярны.
Изображение only pure true norwegian blackx Изображение
Реклама
srg320
Встал на лапы
Сообщения: 85
Зарегистрирован: Пт фев 01, 2013 17:47:26
Откуда: Украина, Луганская область

Re: Реализация на C++ аналогов операторов is и as из C#

Сообщение srg320 »

blackx, спасибо, про dynamic_cast не подумал, попробую как с ним дела обстоят в IARе, если сработает, то можно им заменить as.
второй вариант с перегрузкой ненужного оператора % конечно красивый, но не сработает, т.к. в правой части должен быть тип, а не объект
srg320
Встал на лапы
Сообщения: 85
Зарегистрирован: Пт фев 01, 2013 17:47:26
Откуда: Украина, Луганская область

Re: Реализация на C++ аналогов операторов is и as из C#

Сообщение srg320 »

вобщем, остановился пока на таком варианте:

Код: Выделить всё

class Object
{
public:
	Object(){}
	virtual ~Object(){}

	template<typename type>
	bool is()
	{
		return (dynamic_cast<type*>(this) != NULL);
	}

	template<typename type>
	type* as()
	{
		return dynamic_cast<type*>(this);
	}
};

#define Is(type) is<type>()
#define As(type) as<type>()

class Control : public Object;
class Form : public Control

void main()
{
bool res;
Object* o = new Object();
Control* c = new Control();
Form* f = new Form();
res = o->Is(Object);
res = o->Is(Control);
res = o->Is(Form);

res = c->Is(Object);
res = c->Is(Control);
res = c->Is(Form);

res = f->Is(Object);
res = f->Is(Control);
res = f->Is(Form);

Control* temp;
temp = o->As(Control);		//temp=0
temp = f->As(Control);		//temp=f
}
blackx, ещё раз спасибо
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
blackx
Говорящий с текстолитом
Сообщения: 1518
Зарегистрирован: Пт дек 28, 2012 21:56:46
Откуда: St. Petersburg

Re: Реализация на C++ аналогов операторов is и as из C#

Сообщение blackx »

Рад, что смог натолкнуть на нужные мысли. Видимо добиться на 100% такого же синтаксиса совсем никак получится, но все равно выглядит очень неплохо :))
Изображение only pure true norwegian blackx Изображение
Реклама
Ответить

Вернуться в «Разные вопросы по МК»