замена аргументов командной строки int arg c и char ** argv на std :: vector - PullRequest
3 голосов
/ 03 февраля 2020

После этого сообщения , где я нашел временное решение для моей другой проблемы , я хочу знать, могу ли я заменить int argc, char** argv на std::vector<std::string> переменную / object.

Рассмотрим воображаемый код:

#include <iostream>
#include <CloseLibrary>

void someFunction(int argc, char** argv){
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }
}

int myFunc(int argc, char** argv){
    someFunction(argc, argv);

    return 0;
}

, где CloseLibrary - закрытая библиотека, к которой у меня нет доступа к исходному коду, а функция someFunction из эта библиотека требует аргументов командной строки int argc, char** argv. Но по по какой-то причине У меня не может быть двойных указателей char** в моем коде.

Здесь, в этом посте предлагается нечто вроде того, что мне нужно, но я не знаю, как это использовать. Можно ли написать код следующим образом:

#include <iostream>
#include <CloseLibrary>
#include <vector>

void someFunction(int argc, char** argv){
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }
}

int myFunc("args", [](std::vector<std::string> args){
    std::vector<char *> cstrs;
    cstrs.reserve(args.size());
    for (auto &s : args) cstrs.push_back(const_cast<char *>(s.c_str()));
    someFunction(cstrs.size(), cstrs.data());

    return 0;
}

Или, может быть, есть более канонический способ сделать это? Буду признателен, если вы поможете мне найти правильный способ сделать это и понять решение. Заранее благодарим за помощь.

PS1. Метод char* argv[] подходит для тела функции, но не подходит для входных данных. Я не знаю, почему Pybind11 делает это!

PS2. Здесь, на pybind11 gitter , было предложено:

void run(const std::vector<std::string>& args) {
    for(auto&& e : args) std::cout << e << '\n';
}

PS3. Также предлагается на pybind11 Гиттер:

char** argv = new char*[vec.size()]; // just like malloc(sizeof(char*)*vec.size());
for (int i = 0; i < vec.size(), i++) {
    argv[i] = new char[vec[i].size()];
    memcpy(argv[i], vec[i].data(), vec[i].size()); // or strcpy
}

Ответы [ 2 ]

8 голосов
/ 03 февраля 2020

Вы можете использовать конструктор, который инициализирует вектор из заданного диапазона, при этом параметр argv действует как начальный итератор, а argv+argc действует как конечный итератор.

Например, я обычно начинаю свою основную функцию с:

int main( int argc, char* argv[] )
{
    std::vector< std::string > args( argv, argv + argc );

    for ( auto s : args )
    {
        std::cout << s << std::endl;
    }
}

Обратите внимание, что это также захватит первый аргумент (argv [0]), который обычно (но не обязательно) содержит имя приложения при его запуске.

В вашем случае вы хотели бы сделать обратное, создать непрерывный массив char* из std :: vector . Я бы сделал что-то вроде:

std::vector< char* > rargs( args.size(), 0 ); // Initialize N nullptrs.
for ( int i=0; i<args.size(); ++i )
{
    std::strcpy( rargs[i], args[i].c_str() ); // One-by-one strcpy them 
}

И тогда вы можете передать их в функцию, принимающую arg c, argv как

someFunction( rargs.size(), rargs.data() );
2 голосов
/ 04 февраля 2020

Для чего это стоит ... Вернемся полностью к вашей первоначальной проблеме невозможности использования char** с pybind11, полным рабочим примером, очищенным от кусков, которые вы разместили ниже. Да, это не красиво, но работа с указателями никогда не бывает.

#include <pybind11/pybind11.h>
#include <iostream>

#if PY_VERSION_HEX < 0x03000000
#define MyPyText_AsString PyString_AsString
#else
#define MyPyText_AsString PyUnicode_AsUTF8
#endif

namespace py = pybind11;

void closed_func(int argc, char** argv){
    for (int i = 0; i < argc; ++i) {
        std::cout << "FROM C++: " << argv[i] << std::endl;
    }
}

void closed_func_wrap(py::object pyargv11) {
    int argc = 0;
    std::unique_ptr<char*[]> argv;

// convert input list to C/C++ argc/argv
    PyObject* pyargv = pyargv11.ptr();
    if (PySequence_Check(pyargv)) {
        Py_ssize_t sz = PySequence_Size(pyargv);
        argc = (int)sz;
        argv = std::unique_ptr<char*[]>{new char*[sz]};
        for (Py_ssize_t i = 0; i < sz; ++i) {
            PyObject* item = PySequence_GetItem(pyargv, i);
            argv[i] = (char*)MyPyText_AsString(item);
            Py_DECREF(item);
            if (!argv[i] || PyErr_Occurred()) {
                argv = nullptr;
                break;
            }
        }
    }

// bail if failed to convert
    if (!argv) {
        std::cerr << "argument is not a sequence of strings" << std::endl;
        return;
    }

// call the closed function with the proper types
    closed_func(argc, argv.get());
}

PYBIND11_MODULE(HelloEposCmd, m)
{
    m.def("run", &closed_func_wrap, "runs the HelloEposCmd");
}

Который после компиляции можно использовать как положено:

$ python - a b c d=13
>>> import HelloEposCmd
>>> import sys
>>> HelloEposCmd.run(sys.argv)
FROM C++: -
FROM C++: a
FROM C++: b
FROM C++: c
FROM C++: d=13
>>> 
...