Тестирование, что записано в Java OutputStream

Вам не нужно создавать какие-либо объекты, создающие экземпляры 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 ++.

25
задан Ciryon 22 October 2008 в 09:53
поделиться

3 ответа

Используйте ByteArrayOutputStream и затем вытащите данные из того использования toByteArray () . Это не протестирует , как это пишет в поток (один байт за один раз или как большой буфер), но обычно Вы не должны заботиться об этом так или иначе.

28
ответ дан 28 November 2019 в 21:32
поделиться

Если бы можно передать Писателя 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());
}
3
ответ дан 28 November 2019 в 21:32
поделиться

Это просто. Как сказал @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));
3
ответ дан 28 November 2019 в 21:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: