Это очень странно.
OSX 10.10
LLVM 6.0
XCode 6.1
test_assert("Wierd", String{"ABC"}, "ABC" ); // claims not equal
String — это мой пользовательский класс (упаковывающий примитив Python String), и он должен пройти этот тест.
Вот test_assert с добавленным отладочным выводом:
template <typename B, typename V>
static void test_assert( std::string description, B benchmark, V value )
{
std::ostringstream full_description;
full_description << description
<< " : { " << "benchmark" << ", " << "value" << " }"<< " = { " << typeid(B).name() << ", " << typeid(V).name() << " }"<< " , { " << benchmark << ", " << value << " }";
// N2Py6StringE, PKc i.e. Py::String and const char* (Pointer to Konst Char)
std::cout << typeid(B).name() << ", " << typeid(V).name() << std::endl;
V b_as_v{static_cast<V>(benchmark)};
// wtf? b_as_v: \352\277_\377 -- should be "ABC"std::cout << "b_as_v: " << b_as_v << std::endl; // Y
if( b_as_v == value )
std::cout << " PASSED: " << full_description.str() << std::endl;
else
throw TestError( full_description.str() );
}
Именно это b_as_v{static_cast<V>(benchmark)};
это бросает меня, потому что, если я сделаю один шаг, это корректно приведет меня к оператору String ‘convert to const char *’, который правильно выполняет свои обязанности:
class String : Object {
explicit operator const char*() const
{
std::string s{ as_std_string() };
const char* c{ s.c_str() };
// c before return: ABC
std::cout << "c before return: " << c << std::endl; // X
return c;
}
:
Теперь это странная вещь: если строка X на месте, строка Y ничего не сообщает: ‘b_as_v:’
Удаляя его, строка Y сообщает оригинал: ‘b_as_v: \ 352 \ 277_ \ 377’
На самом деле, просто печать std::cout << std::endl; // X'
для X достаточно, чтобы очистить вывод от Y (однако перемещение X ‘непосредственно перед Y восстанавливает исходное поведение).
Таким образом, похоже, что акт наблюдения изменяет возвращаемое значение.
Гейзенбаг>: |
И ни одно поведение не является желательным.
Еще одна странность заключается в том, что есть дополнительный символ Unicode, который копируется в мой буфер обмена в конце «\ 352 \ 277_ \ 377», если я копирую вставить из консоли Xcode в окно редактирования текста SO.
Даже если я выберу только последний 7
он все еще копирует, даже если он не занимает пробел в консоли XCode.
(Этот дополнительный символ не появляется в SO вопросе, на самом деле его больше нет, когда я снова открываю вопрос для редактирования. Это не
символ новой строки — я тестировал вставку копий в последний символ определенной строки)
Я попытался создать тестовый сценарий, но он работает, к сожалению, как я ожидал: http://ideone.com/gbyU6Y
Довольно сложная настройка, но причина довольно проста:
explicit operator const char*() const
{
std::string s{ as_std_string() };
const char* c{ s.c_str() };
// c before return: ABC
std::cout << "c before return: " << c << std::endl; // X
return c;
}
Указатель возвращается std::string::c_str()
указывает на std::string
внутреннее хранилище, и поэтому может быть признано недействительным по ряду причин — уничтожение std::string
объект является одним из них. Вот, c
становится недействительным, как только ваша функция преобразования и s
уничтожен, это означает, что висячий указатель возвращается.
Кроме того, libc ++ использует оптимизацию небольших строк, а это означает, что строка "ABC"
хранится внутри std::string
сам объект (в данном случае, в стеке), а не в динамически выделенном хранилище. Это значительно повышает вероятность того, что пространство, которое раньше занимало строка, может быть повторно использовано до того, как ваш код попытается ее распечатать.