четверг, 1 августа 2013 г.

Использование перегрузки операторов для enum в C++

На форуме stackoverflow.com можно найти массу советов по тому как вывести имя элемента enum в поток, но практически все они весьма сложные и громоздкие. Однако эту задачу можно решить гораздо проще используя средства самого языка.
Описанный ниже прием я узнал из книги Б. Страуструпа "Дизайн и эволюция С++" (проведя до этого не один час в раздумьях).
Не многие знают, что для типа enum в С++ есть возможность перегружать операторы. Особенно полезно это свойство когда вы генерируете некоторое выражение, например, на SQL.
В данной статье я покажу как можно использовать эту возможность языка С++ на примере вывода имени элемента перечисления в поток.


#include <iostream>
#include <string>
#include <stdexcept>

enum SqlOp
{
	Equal, 
	NotEqual,
	Less,
	Greater,
	LessEqual,
	GreaterEqual
};

inline std::istream& operator >> (std::istream& in, SqlOp& op)
{
	std::string s;
	in >> s;
	if (s == "=") {
		op = Equal;
	} 
	else if (s == "<>") {
		op = NotEqual;
	} 
	else if (s == "<") {
		op = Less;
	}
	else if (s == ">") {
		op = Greater;
	}
	else if (s == "<=") {
		op = LessEqual;
	}
	else if (s == ">=") {
		op = GreaterEqual;
	} else {
		throw std::runtime_error("Incorrect input");
	}
	return in;
}

inline std::ostream& operator << (std::ostream& out, const SqlOp& op)
{
	switch(op)
	{
	case Equal: 
		out << "=";
		break;
	case NotEqual:
		out << "<>";
		break;
	case Less:
		out << "<";
		break;
	case Greater:
		out << ">";
		break;
	case LessEqual:
		out << "<=";
		break;
	case GreaterEqual:
		out << ">=";
		break;
	}
	return out;
}


int main(int argc, char * argv[])
{
	using namespace std;
	SqlOp op = Equal;
	while(cin) {
		try {
			cin >> op;
		} catch(std::exception& e) {
			cout << "error: " << e.what() << endl;
			return 1;
		}
		cout << op << ": " << static_cast<int>(op) << endl;
	}
	return 0;
}


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

enum SqlOp
{
	Equal, 
	NotEqual,
	Less,
	Greater,
	LessEqual,
	GreaterEqual
};

template<typename _IStream>
inline _IStream& operator >> (_IStream& in, SqlOp& op)
{
	std::string s;
	in >> s;
	if (s == "=") {
		op = Equal;
	} 
	else if (s == "<>") {
		op = NotEqual;
	} 
	else if (s == "<") {
		op = Less;
	}
	else if (s == ">") {
		op = Greater;
	}
	else if (s == "<=") {
		op = LessEqual;
	}
	else if (s == ">=") {
		op = GreaterEqual;
	} else {
		throw std::runtime_error("Incorrect input");
	}
	return in;
}

template<typename _OStream>
inline _OStream& operator << (_OStream& out, const SqlOp& op)
{
	switch(op)
	{
	case Equal: 
		out << "=";
		break;
	case NotEqual:
		out << "<>";
		break;
	case Less:
		out << "<";
		break;
	case Greater:
		out << ">";
		break;
	case LessEqual:
		out << "<=";
		break;
	case GreaterEqual:
		out << ">=";
		break;
	}
	return out;
}

Теперь мы можем использовать, например, потоки из библиотеки Qt для ввода/вывода нашего перечисления. Но и это еще не все: мы также можем использовать перегрузку операторов +, -, * и всех остальных операторов языка C++ разрешенных к перегрузке в пользовательских типах!

Комментариев нет:

Отправить комментарий