Как мы можем вызвать функцию с параметром = значение в С++?

При чтении кодов мы найдем некоторые функции, подобные этому.

g_spawn_async(NULL, new_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);

Я думаю, никто не может понять, что означает каждый параметр. Чтобы понять код, нам нужно найти объявление функции.

gboolean    g_spawn_async               (const gchar *working_directory,
                                         gchar **argv,
                                         gchar **envp,
                                         GSpawnFlags flags,
                                         GSpawnChildSetupFunc child_setup,
                                         gpointer user_data,
                                         GPid *child_pid,
                                         GError **error);

Как мы можем вызвать функцию, подобную следующему формату в С++?

g_spawn_async(working_directory=NULL,
              argv=new_argv,
              envp=NULL,
              flags=G_SPAWN_SEARCH_PATH,
              child_setup=NULL,
              user_data=NULL,
              child_pid=NULL,
              error=NULL);

Я думаю, что этот будет более читабельным, и я могу понять код, не ища объявления функции.

Я знаю, что Python может это сделать. Как С++ может это сделать?

Ответ 1

С++ не поддерживает это изначально, поэтому вы не можете сделать это с помощью любой старой существующей функции. Если вы создаете свой собственный API, вы можете использовать то, что называется Именованный идентификатор параметров, чтобы подражать ему. Пример из ссылки:

File f = OpenFile("foo.txt")
           .readonly()
           .createIfNotExist()
           .appendWhenWriting()
           .blockSize(1024)
           .unbuffered()
           .exclusiveAccess();

Ответ 2

Это невозможно в C или С++.

Я понимаю ваши боли с этим. Я лично считаю, что это признак плохого дизайна, чтобы функция взяла на себя 9000 аргументов, особенно если большинство из них являются значениями NULL или placeholder. Многие стандартизованные функции POSIX, например, берут какой-то struct, который накапливает все необходимые значения в один, легко понятный аргумент.

Ответ 3

Нет, это невозможно. Но вы можете назначить значения NULL для переменных, а затем передать их в качестве параметров, если это поможет с вашей читабельностью!

g_spawn_async(working_directory, argv, envp,flags,child_setup , user_data, child_pid, error);

Ответ 5

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

enum MyFuncParamId
{
    myA,
    myB,
    myC,
    myD,
    unknown
};

class MyFuncParam
{
    union OneParam
    {
        double aOrB;
        C c;
        int d;

        OneParam() {}
        ~OneParam() {}
    };
    OneParam myParam;
    MyFuncParamId myId;

public:
    MyFuncParam( MyFuncParamId id, double value )
        : myId( id )
    {
        switch ( myId ) {
        case myA:
        case myB:
            myParam.aOrB = value;
            break;

        case myC:
            assert( 0 );
            abort();

        case myD:
            myParam.d = value;
            break;
        }
    }
    MyFuncParam( MyFuncParamId id, C const& value )
        : myId( id )
    {
        switch ( myId ) {
        case myA:
        case myB:
        case myD:
            assert( 0 );
            abort();

        case myC:
            new (&myParam.c) C( value );
            break;
        }
    }
    MyFuncParam( MyFuncParamId id, int value )
        : myId( id )
    {
        switch ( myId ) {
        case myA:
        case myB:
            myParam.aOrB = value;
            break;

        case myC:
            assert( 0 );
            abort();

        case myD:
            myParam.d = value;
            break;
        }
    }
    MyFuncParam( MyFuncParam const& other )
        : myId( other.myId )
    {
        switch ( myId ) {
        case myA:
        case myB:
            myParam.aOrB = other.myParam.aOrB;
            break;
        case myC:
            new (&myParam.c) C( other.myParam.c );
            break;
        case myD:
            myParam.d = other.myParam.d;
            break;
        }
    }
    ~MyFuncParam()
    {
        switch( myId ) {
        case myC:
            myParam.c.~C();
            break;
        }
    }
    MyFuncParam& operator=( MyFuncParam const& ) = delete;
    friend class MyFuncParamGroup;
};

class MyFuncRouter
{
    MyFuncParamId myId;
public:
    MyFuncRouter( MyFuncParamId id ) : myId( id ) {}

    MyFuncParam operator=( double value )
    {
        return MyFuncParam( myId, value );
    }

    MyFuncParam operator=( C const& value )
    {
        return MyFuncParam( myId, value );
    }

    MyFuncParam operator=( int value )
    {
        return MyFuncParam( myId, value );
    }
};

static MyFuncRouter a( myA );
static MyFuncRouter b( myB );
static MyFuncRouter c( myC );
static MyFuncRouter d( myD );

struct MyFuncParamGroup
{
    bool aSet;
    bool bSet;
    bool cSet;
    bool dSet;
    double a;
    double b;
    C c;
    int d;
    MyFuncParamGroup()
        : aSet( false )
        , bSet( false )
        , cSet( false )
        , dSet( false )
    {
    }
    void set( MyFuncParam const& param )
    {
        switch ( param.myId ) {
        case myA:
            assert( !aSet );
            aSet = true;
            a = param.myParam.aOrB;
            break;
        case myB:
            assert( !bSet );
            bSet = true;
            b = param.myParam.aOrB;
            break;
        case myC:
            assert( !cSet );
            cSet = true;
            c = param.myParam.c;
            break;
        case myD:
            assert( !dSet );
            dSet = true;
            d = param.myParam.d;
            break;
        }
    }
};

void
myFunc(
    MyFuncParam const& p1,
    MyFuncParam const& p2,
    MyFuncParam const& p3,
    MyFuncParam const& p4)
{
    MyFuncParamGroup params;
    params.set( p1 );
    params.set( p2 );
    params.set( p3 );
    params.set( p4 );
    std::cout << "a = " << params.a
        << ", b = " << params.b
        << ", c = " << params.c
        << ", d = " << params.d
        << std::endl;
}

Примечания:

  • Я использовал С++ 11 здесь. То же самое можно сделать и в более ранних версии С++, заменив тип C на union на unsigned char c[sizeof( C )];, добавив что-то к union для обеспечения правильного выравнивания (при необходимости) и большого количества типов литье.

  • Это было бы намного проще с boost::variant (вместо union) и boost::optionalMyFuncParamGroup). У меня не было Boost, поэтому я сделал большую часть того, что они делают в явном виде. (Это, конечно, делает код намного дольше.)

  • Я намеренно использовал два параметра с одним типом, и пользовательский тип (C) с нетривиальными конструкторами, чтобы показать, как они обрабатываются.

  • Вероятно, вы захотите немного инкапсуляции, и немного больше проверка ошибок.

Но реальный вопрос: вы действительно хотите пойти по этому маршруту? Количество кода увеличивается линейно с количеством параметры. И с любым достойным редактором вы можете временно поставить список параметров из объявления функции справа от ваш экран и заполнить параметры слева, непосредственно вдоль стороны объявления параметра. (В gvim я обычно используйте для этого режим редактирования блока, но :vsplit также может быть используется.)

Ответ 6

Именованные параметры очень полезны, и я даже думал, что на языке они должны быть единственным способом вызова функции, за исключением одного очевидного параметра, если это присутствует.

sin(x)               // the obvious main parameter
sin(x, unit=DEGREE)  // any other must be named

У С++, к сожалению, их нет.

Более важно, что С++ не обладает способностью метапрограммирования, необходимой для их реализации.

В то время как есть трюки, которые могут попытаться несколько имитировать именованные параметры, и даже если полученный код может выглядеть почти разумным, то, что совершенно неприлично, - это код, который вам нужно написать, чтобы получить эту симуляцию и что на С++ нет никакого способа сгенерируйте этот код.

Я имею в виду то, что, хотя возможно написать функцию, принимающую именованные параметры, эмулированные разумно, код для самой функции является отвратительным и не может быть автоматически сгенерирован в С++. Это означает, что никто не будет писать функции таким образом, чтобы функция все еще не присутствовала.

Моя догадка заключается в том, что именованные параметры отсутствуют из-за сочетания незнания (они существовали до того, как С++ был изобретен, возможно, еще до C) и гордости (даже в С++ 11 возможности метапрограммирования языка патетичны и вещи тривиальны например, перечислять во время компиляции элементы структуры или параметры функции невозможно).