簡體   English   中英

C / C ++中的變量函數和參數賦值

[英]Variadic functions and arguments assignment in C/C++

我想知道在C / C ++語言中是否可以以鍵值形式將參數傳遞給函數。 例如在python中你可以這樣做:

def some_function(arg0 = "default_value", arg1):
    # (...)

value1 = "passed_value"
some_function(arg1 = value1)

所以C中的替代代碼可能如下所示:

void some_function(char *arg0 = "default_value", char *arg1)
{
    ;
}

int main()
{
    char *value1 = "passed_value";
    some_function(arg1 = value1);
    return(0);
}

所以在some_function中使用的參數是:

arg0 =“default_value”

arg1 =“pass_value”

有任何想法嗎?

這是使用復合文字和可變參數宏的C99解決方案:

#include <stdio.h>

#define some_func(...) some_func_((struct some_func_args_){ __VA_ARGS__ })

struct some_func_args_
{
    const char *arg1;
    const char *arg2;
};

static void some_func_(struct some_func_args_ args)
{
    if(!args.arg1) args.arg1 = "default";
    printf("---\narg1 = %s\narg2 = %s\n", args.arg1, args.arg2);
}

int main(void)
{
    some_func("foo", "bar");
    some_func(.arg1 = "spam");
    some_func(.arg2 = "eggs");
    return 0;
}

您可以使用以下方法在C ++中模擬它:

struct params {
   string foo_;
   double bar_;
   short  xxx_;
   params() : foo_("123"), bar_(3.1415), xxx_(42) {} // default parameters
   params& foo(string s) {foo_=s;return *this;}
   params& bar(double x) {bar_=x;return *this;}
   params& xxx(short x) {xxx_=x;return *this;}
};

void some_function(params const & p);

int main() {
   some_function(params().bar(99.9).xxx(23));
}

但恕我直言,這不值得努力。 鍋爐板太多了。

如果我沒記錯的話,Stroustrup的書“C ++的設計和演變”包含了一個討論“命名參數”的這個特征請求的部分。 結論類似於:不是一個好主意。 如果您需要詳細信息,請查看它。

C中不支持命名參數,但您可以使用variadic函數模擬命名參數(盡管您松散了類型安全性):

#include <stdarg.h>

void do_sth (int foo, ...)
{
    int baz = 7;             /* "baz" argument */
    const char *xyz = "xyz"; /* "xyz" argument */

    /* Parse named parameters */
    va_list ap;
    va_start (ap, foo);
    for (;;) {
        const char *key = va_arg (ap, char *);
        if (key == NULL) {
            /* Terminator */
            break;
        } else if (strcmp (key, "baz") == 0) {
            baz = va_arg (ap, int);
        } else if (strcmp (key, "xyz") == 0) {
            xyz = va_arg (ap, char *);
        } else {
            /* Handle error */
        }
    }
    va_end (ap);

    /* do something useful */
}

do_sth (1, NULL);                             // no named parameters
do_sth (2, "baz", 12, NULL);                  // baz = 12
do_sth (3, "xyz", "foobaz", NULL);            // xyz = "foobaz"
do_sth (4, "baz", 12, "xyz", "foobaz", NULL); // baz = 12, xyz = "foobaz"

只記得用NULL結束可選參數列表。 您也可以使用一些ENUM作為鍵而不是字符串。

在GTK +中,此技術用於exacmle:

這在vanilla C或C ++中不可用。 但是,有一個C ++ Boost庫可以讓你為你編寫的函數執行此操作: Boost.Parameter 借用他們的一個例子,它的用法有點像這樣:

myclass x("bob", 3);                     // positional
myclass y(_index = 12, _name = "sally"); // named
myclass z("june");                       // positional/defaulted

但是,為您的功能實現這一點看起來確實有些麻煩。 你可能認為不值得努力。

不,您不能在C或C ++中按名稱傳遞參數。

兩種語言都支持函數中尾隨參數的默認參數。 也就是說,函數的任何參數都可以具有默認值,但是在找到第一個默認值之后的所有后續參數也必須具有默認值。

例如,以下內容均有效:

void f(int a, int b = 0);
void g(double a = 1, double b = 2);
void h(int a = 3, int b = 2, int c = 1, int d = 0);
void i(float a, float b, float c = 1, float d = 2);

但以下都不是有效的:

void j(int a = 1, int b);
void k(int a, int b = 1, int c);
void l(int a = 2, int b = 1, int c);

原因很簡單:由於您無法在C和C ++中按名稱傳遞參數,因此了解哪些參數使用默認值以及使用傳遞值的唯一方法是將傳遞的值按順序分配給參數,然后使用任何遺留的默認值(按順序)。

因此,以下調用使用上述函數有效:

f(0); // calls f(0, 1);
g(); // calls g(1,2);
g(10); // calls g(10,2);
h(); // calls h(3,2,1,0);
h(1,2); // calls h(1,2,1,0);
#define call_foo(...) \
   do { \
      int first; \
      int second; \
      int third;  \
      (__VA_ARGS__); \
      foo(first, second, third); \
   } while (0)

....

int main(void) {
   call_foo(first=9, third=5, second=3);
}

以非笨重的方式獲得返回值更加困難。

這將允許你做非常愚蠢的事情(我討厭),例如為你的參數指定默認值:

#define call_foo(...) \
   do { \
      int first = default_first; \
      int second; \
      int third;  \
      (__VA_ARGS__); \
      foo(first, second, third); \
   } while (0)

如果你這樣做,這將失敗:

   int first = 9;
   call_foo(first=first, third=5, second=3);

因為在宏擴展中, first=first意味着first用自己初始化本地。 我想過嘗試使用預處理器連接來解決這個問題,但這會變得更加復雜。 您必須列出與函數參數相同數量的宏參數。

#define call_foo(x, y , z) \
   do { \
      int call_foo_first; \
      int call_foo_second; \
      int call_foo_third;  \
      call_foo_##x;   \
      call_foo_##y;   \
      call_foo_##z;   \
      foo(call_foo_first, call_foo_second, call_foo_third); \
   } while (0)

call_foo_##x; line會變成call_foo_first=first; 如果在上一個示例中調用,則每個符號都有自己的唯一名稱。 這使得默認參數技巧更加笨拙,因為你必須指定一些東西來填充宏參數列表中的那個點。

如果宏是與默認參數定義的first則:

call_foo(first, third=7, second=8);

這會強制您列出所有參數(或者至少會導致合法C的某些參數 - 您可能已經列出了兩次,但無論如何都可以這樣做),以便使用此宏。

我認為你應該能夠為變量參數列出的宏/函數擴展它的最后一個版本,但是仍然必須在列表的末尾傳遞可選參數,你必須在之前用完所有的強制點你得到了可選的參數。

僅限C ++解決方案:您可以使用boost :: any,std :: map和std :: string來創建具有posible參數的對象。

#include <iostream>
#include <map>
#include <string>

#include "boost/any.hpp"

using namespace std;

void variableArguments(map<string, boost::any> arguments){
    cout << ">>> variableArguments begins" << endl;

    if(arguments.find("intParameter") != arguments.end()){
    cout << "Found int parameter: " 
         << boost::any_cast<int>(arguments["intParameter"]) << endl;
    }

    if(arguments.find("stringParameter") != arguments.end()){
    cout << "Found string parameter: " 
         << boost::any_cast<string>(arguments["stringParameter"]) << endl;
    }

    cout << "<<< variableArguments ends" << endl;

}

int main(int argc, char *argv[])
{

    map<string, boost::any> argMap;
    argMap["intParameter"] = 5;
    argMap["stringParameter"] = string("MyString");

    variableArguments(argMap);

    argMap.erase(argMap.find("intParameter"));

    variableArguments(argMap);

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM