Необходимо сделать контекст доступным для операторов вставки C ++ Ostream
Вопрос
Для API, над которым я работаю, я хочу позволить пользователю вставить пользовательские объекты в ostream
, но эти объекты не имеют никакого значения самостоятельно и слишком ограничены памятью, чтобы включить дополнительный указатель или ссылку на контекст. (Подумайте о десятках миллионов 16-/32-/48-битных объектов во встроенной системе с ограниченной памятью.)
Предположим, что пользователь инициализирует базовый контекст и смотрит один из этих объектов:
DDB ddb("xc5vlx330t");
Tilewire tw = ddb.lookUpTilewire("DSP_X34Y0", "DSP_IMUX_B5_3");
...
std::cout << complexDataStructure;
В совершенно иной области, возможно, вложенной далеко от явного кода пользователя, нам может потребоваться вставить объект в ostream
, с ddb
недоступен.
os << tw;
Фактическое значение, заключенное в TW 97,594,974
, но желаемый вывод таков:
DSP_IMUX_B5_3@[263,84] DSP "DSP_X34Y0" (1488@77406)
Чтобы это работало, соответствующему оператору вставки потребуется доступ к ddb
, но он не может полагаться на статические или глобальные переменные или функции (по соображениям многопоточности). Что я нравится Сделать это, чтобы пользователь запросил и использовать потоковую обертку, как это:
ostream& wrappedCout = ddb.getWrappedOstream(std::cout);
Возвращенный подкласс Ostream будет включать в себя ссылку на DDB для использования специальными потоковыми вставками, которые нуждались в нем, и ссылку на исходный поток -std::cout
В этом случае - где это перестановило бы все свои результаты.
К сожалению, схемы наследства или композиции, которые я придумал, грязны, чтобы кодировать (не огромное беспокойство) и, возможно, проблематичны для пользователя (гораздо большее беспокойство). Любые предложения о том, как элегантно сделать DDB доступным для операторов вставки? Я незначительно осведомлен о Boost.iostreams, но не уверен, что это поможет мне здесь.
Решение
Напишите пользовательский манипулятор потока, в котором хранится ссылка на DDB, используя механизм IWord/Pword. Вот пример, вам нужно добавить блокировку вокруг карты iWork_Indexes в многопоточной программе.
class dbb
{
public:
explicit dbb(int value) : m_value(value) {}
int value() const { return m_value; }
private:
int m_value;
};
class dbb_reliant_type
{
public:
dbb_reliant_type(const std::string& value) : m_value(value) {}
const std::string& value() const { return m_value; }
private:
std::string m_value;
};
typedef std::map<std::ostream*, int> iword_map;
iword_map iword_indexes;
inline int get_iword_index(std::ostream& os)
{
iword_map::const_iterator index = iword_indexes.find(&os);
if(index == iword_indexes.end())
{
std::pair<iword_map::iterator, bool> inserted = iword_indexes.insert(std::make_pair(&os, os.xalloc()));
index = inserted.first;
}
return index->second;
}
inline std::ostream& operator<<(std::ostream& os, const dbb& value)
{
const int index = get_iword_index(os);
if(os.pword(index) == 0)
os.pword(index) = &const_cast<dbb&>(value);
return os;
}
std::ostream& operator<<(std::ostream& os, const dbb_reliant_type& value)
{
const int index = get_iword_index(os);
dbb* deebeebee = reinterpret_cast<dbb*>(os.pword(index));
os << value.value() << "(" << deebeebee->value() << ")";
return os;
}
int main(int, char**)
{
dbb deebeebee(5);
dbb_reliant_type variable("blah");
std::cout << deebeebee << variable << std::endl;
return 0;
}
Другие советы
Я не совсем уверен, понимаю ли я, что можно получить в какое время и что может и не могу измениться, но ... Можете ли вы сделать что -то подобное
struct TilewireFormatter {
DDB *ddb;
TilewireFormatter(DDB* d) : ddb(d) {}
print(std::ostream& out, const Tilewire& obj) {
// some formatting dependent on ddb
out << obj;
}
};
и заменить out << tw;
с formatter.print(out, tw)
;
Затем не предоставляйте какую -либо перегрузку оператора для Tilewire и пропустите экземпляр TileWireFormatter вокруг, который используется для их форматирования на основе того, что такое DDB?
Вместо того, чтобы разбираться и пытаться найти способ передать контекстную информацию при использовании оператора вставки, я предлагаю сделать что -то вроде метода печати, как предполагает choobablue. Это хорошее и простое решение, и все, что более причудливое, вероятно, больше проблем, чем стоит.
Я также считаю странным, что вы выбираете iostreams для встроенной системы. Они одна из самых раздутых частей стандартной библиотеки C ++ (не только по внедрению, но и по замыслу), и если вы работаете над встроенной системой, вы можете с такой же Базовый дизайн iostreams) и, вероятно, может сделать это так же быстро, как и попытка использовать iostream эффективно и по нескольким потокам.
Я новичок в этом, поэтому в случае предоставления моего собственного ответа мешает мне поделиться кредью с Гэри, ну, Гэри указал на то, что я только что наткнулся на минуты до той же ссылки: Stream Storage для частного использования: iword, pword и xalloc
#include <iostream>
// statically request a storage spot that can be associated with any stream
const int iosDdbIndex = std::ios_base::xalloc();
class DDB {
public:
// give the stream a pointer to ourselves
void bless(std::ostream& os) { os.pword(iosDdbIndex) = this; }
// provide a function that the insertion operator can access
int getSomething(void) { return 50; }
};
class Tilewire {
friend std::ostream& operator<< (std::ostream& os, Tilewire tilewire);
// encapsulate a dummy value
int m;
public:
// construct the Tilewire
Tilewire(int m) : m(m) {}
};
std::ostream& operator<< (std::ostream& os, Tilewire tilewire) {
// look up the pointer to the DDB object
DDB* ddbPtr = (DDB*) os.pword(iosDdbIndex);
// insert normally, and prove that we can access the DDB object's methods
return os << "Tilewire(" << tilewire.m << ") with DDB param " << ddbPtr->getSomething();
}
int main (int argc, char * const argv[]) {
DDB ddb;
ddb.bless(std::cout);
std::cout << Tilewire(0) << std::endl;
return 0;
}