SWIG и PHP7: странное поведение строкового вектора

Я пишу некоторый код-оболочку, используя SWIG, чтобы выставить мои функции C++ для PHP.

мой_модуль.i

%module phpMyModule

%include "exception.i"
%include "std_string.i"
%include "typemaps.i"

// INPUT: convert PHP native array to std::vector<std::string>
%typemap(in) const std::vector<std::string> & %{
    if (Z_TYPE($input) == IS_ARRAY) {
        std::vector<std::string> temp2;
        $1 = &temp2;

        HashTable *ht = Z_ARRVAL($input);
        zval *data;
        HashPosition pos;
        for (zend_hash_internal_pointer_reset_ex(ht, &pos);
             (data = zend_hash_get_current_data_ex(ht, &pos)) != nullptr;
             zend_hash_move_forward_ex(ht, &pos)
        ) {
            convert_to_string(data);
            $1->push_back( std::string( Z_STRVAL_P(data), Z_STRLEN_P(data) ) );
        }
    }
    else SWIG_exception( SWIG_TypeError, "Type Error: Only PHP array is supported!" );
%}

%{
#include "MyModule.h"
%}

extern int initialize_engine( const std::string& script_file, const std::vector<std::string>& input_vars );

Созданный SWIG код оболочки

ZEND_NAMED_FUNCTION(_wrap_initialize_engine) {
    std::string *arg1 = 0 ;
    std::vector< std::string > *arg2 = 0 ;
    std::string temp1 ;
    zval args[2];
    int result;

    SWIG_ResetError();
    if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) {
        WRONG_PARAM_COUNT;
    }

    convert_to_string(&args[0]);
    temp1.assign(Z_STRVAL(args[0]), Z_STRLEN(args[0]));
    arg1 = &temp1;

    if (Z_TYPE(args[1]) == IS_ARRAY) {
        std::vector<std::string> temp2;
        arg2 = &temp2;

        HashTable *ht = Z_ARRVAL(args[1]);
        zval *data;
        HashPosition pos;
        for (zend_hash_internal_pointer_reset_ex(ht, &pos);
             (data = zend_hash_get_current_data_ex(ht, &pos)) != nullptr;
             zend_hash_move_forward_ex(ht, &pos)
        ) {
            convert_to_string(data);
            arg2->push_back( std::string( Z_STRVAL_P(data), Z_STRLEN_P(data) ) );
        }
    }
    else SWIG_exception( SWIG_TypeError, "Type Error: Only PHP array is supported!" );

    result = (int)initialize_engine((std::string const &)*arg1,(std::vector< std::string > const &)*arg2);

    RETVAL_LONG(result);
thrown:
    return;
fail:
    SWIG_FAIL();
}

test.php

<?php

    require_once '<path>/<to>/phpMyModule.php';

    $handle = phpMyModule::initialize_engine(
        '<path>/<to>/test.script',
        ["var1", "var2", "var3"]
    );

    echo "Handle #1 Value: $handle\n";
    phpMyModule::terminate_engine($handle);

?>

По сути, приведенный выше код вызывает _wrap_initialize_engine() со строкой PHP (script_file) и массивом имен переменных PHP (input_vars). SWIG использует typemap для преобразования строки и массива PHP в std::string и std::vector соответственно, а затем вызывает настоящий initialize_engine().

В initialize_engine( const std::string& script_file, const std::vector<std::string>& input_vars ) у меня есть:

std::for_each( input_vars.begin(), input_vars.end(), [&]( const std::string& name ) {
    std::cout << "Adding " << name << " ..." << std::endl;
    // signature is Data::addVariable( const std::string& name, const VariableVector& values );
    // Data::VariableVector is actually std::vector<double>
    data.addVariable( name, Data::VariableVector() );
} );

Это работает. Распечатка

Adding var1 ...
Adding var2 ...
...

Но если я закомментирую std::cout ..., все вызовы data.addVariable() получат пустую строку для аргумента name. (Я знаю это, потому что внутри вызова я проверяю имя на соответствие существующим именам и выдаю ошибку, когда используются дубликаты. Без std::cout ... я получил сообщение об ошибке, говорящее, что «имя» уже существует»...)

Мой вопрос

Как это могло случиться? На const std::vector<std::string>& не должно влиять то, вызываю ли я на нем std::cout.

Мое единственное предположение было бы в том, что std::string повторно использовал точки char* вместо их копирования? Если это так, то настоящие буферы все еще находятся внутри PHP и могли быть изменены? Я считаю, что это не должно иметь место, но просто хочу, чтобы кто-то с лучшим знанием C++ подтвердил это для меня.

Но если это было не так, то почему происходит вышеописанное странное поведение?


person yhf8377    schedule 27.11.2017    source источник
comment
std::string делает копию.   -  person    schedule 27.11.2017
comment
Опубликованный код выглядит нормально. Трудно сказать, что может быть причиной проблемы, которую вы видите. Одна вещь, которую вы можете сделать для устранения проблемы, это добавить несколько строк кода для вывода содержимого temp2 непосредственно перед вызовом my_cpp_function.   -  person R Sahu    schedule 27.11.2017
comment
@RSahu Я добавил еще один std::cout прямо перед вызовом my_cpp_function(), и вывод правильный. Однако использование или неиспользование std::cout внутри my_cpp_function по-прежнему приводит к разным результатам.   -  person yhf8377    schedule 27.11.2017
comment
Что такое подпись для data.addVariable()?   -  person Phil Brubaker    schedule 27.11.2017


Ответы (1)


Оказалось, что я сделал глупую ошибку... Извините...

мой_модуль.i

%module phpMyModule

%include "exception.i"
%include "std_string.i"
%include "typemaps.i"

// INPUT: convert PHP native array to std::vector<std::string>
%typemap(in) const std::vector<std::string> & %{
    std::vector<std::string> temp2;  // should be declared here!
    if (Z_TYPE($input) == IS_ARRAY) {
        // if declared here, temp2 will be released before init_engine() was called!
        // Wrong: std::vector<std::string> temp2;
        $1 = &temp2;
        ......
}
else SWIG_exception( SWIG_TypeError, "Type Error: Only PHP array is supported!" );

%}

person yhf8377    schedule 27.11.2017