Вам не нужно создавать какие-либо объекты, создающие экземпляры MyTemplate1
в модуле компиляции, чтобы видеть объекты типа info, описывающие классы реализации этого шаблона, в глобальной таблице символов объектного файла. Вам нужно только сослаться на typeid
такого класса: -
$ cat main.cpp
#include
template
class MyTemplate1
{
public:
T a;
MyTemplate1(T other){
a = other;
}
};
int main(void)
{
return (typeid(MyTemplate1) == typeid(MyTemplate1));
}
$ clang++ -Wall -c main.cpp
$ readelf -s -W main.o | grep MyTemplate1
5: 0000000000000000 16 OBJECT WEAK DEFAULT 15 _ZTI11MyTemplate1IfE
6: 0000000000000000 16 OBJECT WEAK DEFAULT 10 _ZTI11MyTemplate1IiE
7: 0000000000000000 17 OBJECT WEAK DEFAULT 13 _ZTS11MyTemplate1IfE
8: 0000000000000000 17 OBJECT WEAK DEFAULT 8 _ZTS11MyTemplate1IiE
$ c++filt _ZTI11MyTemplate1IfE
typeinfo for MyTemplate1
$ c++filt _ZTI11MyTemplate1IiE
typeinfo for MyTemplate1
$ c++filt _ZTS11MyTemplate1IfE
typeinfo name for MyTemplate1
$ c++filt _ZTS11MyTemplate1IiE
typeinfo name for MyTemplate1
Эти typeinfo
объекты существуют, потому что, как прокомментировал @Peter, стандарт C ++ требует, чтобы typeid
ссылается на объект статической длительности хранения
Что именно происходит под капотом?
blockquote>Вы можете задаться вопросом: почему компилятор делает это
typeinfo
] символы объекта слабые , а не просто глобальные? Почему он определяет их в разных разделах объектного файла? (разделы 10 и 15 моего объектного файла, разделы 2894 и 2899 вашего).И если мы проверим, что еще находится в этих разделах:
$ readelf -s main.o | egrep '(10 |15 )' 5: 0000000000000000 16 OBJECT WEAK DEFAULT 15 _ZTI11MyTemplate1IfE 6: 0000000000000000 16 OBJECT WEAK DEFAULT 10 _ZTI11MyTemplate1IiE
, мы видим, что каждый объект является единственным в своем разделе. Почему так?
В моих
main.o
эти разделы 10 и 15:$ readelf -t main.o | egrep '(\[10\]|\[15\])' [10] .rodata._ZTI11MyTemplate1IiE [15] .rodata._ZTI11MyTemplate1IfE
Каждый из них - только для чтения раздел данных в смысл:
__attribute__((section(.rodata._ZTI11MyTemplate1IiE))) __attribute__((section(.rodata._ZTI11MyTemplate1IfE)))
, который не содержит ничего, кроме определения объекта, после которого он назван.
Компилятор предоставляет каждому из объектов раздел данных для всех себе по той же причине, по которой он создает символы
WEAK
. Ссылки наtypeid(MyTemplate1
для произвольного типа) X
могут быть сделаны в нескольких единицах перевода в пределах одной и той же связи, что#include
определениеMyTemplate1
. Чтобы предотвратить сбой связи в таких случаях с ошибкой множественного определения , компилятор делает символы слабыми. Линкер будет допускать множественные определения слабого символа , разрешая все ссылки просто на первое определение, которое представляет себя, и игнорируя остальные. Посредством выделения уникального раздела данных (или функционального раздела, в зависимости от случая) определению каждого слабого символа, создающего экземпляр шаблона, компилятор дает компоновщику свободу отбрасывать любые избыточные секции данных или функций, которые определяют такой же слабый символ без риска побочного ущерба для программы. См.$ cat MyTemplate1.hpp #pragma once template
class MyTemplate1 { public: T a; MyTemplate1(T other){ a = other; } }; $ cat foo.cpp #include "MyTemplate1.hpp" #include int foo() { return typeid(MyTemplate1 ) == typeid(MyTemplate1 ); } $ cat bar.cpp #include "MyTemplate1.hpp" #include int bar() { return typeid(MyTemplate1 ) != typeid(MyTemplate1 ); } $ cat prog.cpp extern int foo(); extern int bar(); int main() { return foo() && bar(); } Если мы скомпилируем:
$ clang++ -Wall -c prog.cpp foo.cpp bar.cpp
и ссылку (с некоторой диагностикой) следующим образом:
$ clang++ -o prog prog.o bar.o foo.o \ -Wl,-trace-symbol=_ZTI11MyTemplate1IfE \ -Wl,-trace-symbol=_ZTI11MyTemplate1IiE \ -Wl,-Map=mapfile /usr/bin/ld: bar.o: definition of _ZTI11MyTemplate1IfE /usr/bin/ld: bar.o: definition of _ZTI11MyTemplate1IiE /usr/bin/ld: foo.o: reference to _ZTI11MyTemplate1IfE /usr/bin/ld: foo.o: reference to _ZTI11MyTemplate1IiE
ввод
bar.o
доfoo.o
], то компоновщик выбирает определения из_ZTI11MyTemplate1I(f|i)E
изbar.o
и игнорирует определения изfoo.o
, разрешая ссылки вfoo.o
на определения изbar.o
. И файл карты показывает:mapfile (1)
... Discarded input sections ... .rodata._ZTI11MyTemplate1IiE 0x0000000000000000 0x10 foo.o ... .rodata._ZTI11MyTemplate1IfE 0x0000000000000000 0x10 foo.o ...
, что определения в
foo.o
были отброшены. Если мы свяжемся с порядкомbar.o
иfoo.o
в обратном порядке:$ clang++ -o prog prog.o foo.o bar.o \ -Wl,-trace-symbol=_ZTI11MyTemplate1IfE \ -Wl,-trace-symbol=_ZTI11MyTemplate1IiE \ -Wl,-Map=mapfile /usr/bin/ld: foo.o: definition of _ZTI11MyTemplate1IfE /usr/bin/ld: foo.o: definition of _ZTI11MyTemplate1IiE /usr/bin/ld: bar.o: reference to _ZTI11MyTemplate1IfE /usr/bin/ld: bar.o: reference to _ZTI11MyTemplate1IiE
, то мы получим противоположные результаты. Определения из
foo.o
связаны между собой:mapfile (2)
... Discarded input sections ... .rodata._ZTI11MyTemplate1IiE 0x0000000000000000 0x10 bar.o ... .rodata._ZTI11MyTemplate1IfE 0x0000000000000000 0x10 bar.o ...
из
bar.o
выброшены. Этот принцип компоновки «первым пришел - первым обслужен» хорош, потому что - и только , потому что - определениеtemplate
, которое компилятор нашел в блоке переводаMyTemplate1 foo.cpp
, было идентично ] к тому, которое было найдено вbar.cpp
, условие, которое стандарт C ++ требует , в правило единого определения , но которое компилятор C ++ ничего не может сделать для принудительного применения .Вы можете сделать, в основном, те же самые наблюдения относительно символов создания шаблона в целом, и то, что вы видите с помощью clang ++, по сути то же самое, что вы увидите с g ++.
Используйте ByteArrayOutputStream и затем вытащите данные из того использования toByteArray () . Это не протестирует , как это пишет в поток (один байт за один раз или как большой буфер), но обычно Вы не должны заботиться об этом так или иначе.
Если бы можно передать Писателя XmlWriter, я передал бы его StringWriter
. Можно запросить эти StringWriter
содержание с помощью toString()
на нем.
, Если необходимо передать OutputStream
, можно передать ByteArrayOutputStream
, и можно также звонить toString()
на нем для получения его содержания как Строки.
Тогда можно кодировать что-то как:
public void testSomething()
{
Writer sw = new StringWriter();
XmlWriter xw = new XmlWriter(sw);
...
xw.writeString("foo");
...
assertEquals("...<aTag>foo</aTag>...", sw.toString());
}
Это просто. Как сказал @JonSkeet:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// pass the baos to be writed with "value", for this example
byte[] byteArray = baos.toByteArray();
Assert.assertEquals("value", new String(byteArray));