2013-08-21 38 views
12

Khi đọc mã, chúng tôi sẽ tìm thấy một số chức năng như thế này.Làm thế nào chúng ta có thể gọi một hàm với "parameter = value" trong C++?

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

Tôi nghĩ không ai có thể hiểu được ý nghĩa của mọi thông số. Để hiểu mã, chúng ta phải tìm ra khai báo hàm.

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); 

Làm cách nào để gọi hàm như định dạng sau trong C++?

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); 

Tôi nghĩ rằng điều này sẽ dễ đọc hơn và tôi có thể hiểu mã mà không cần tìm kiếm khai báo hàm.

Tôi biết Python có thể thực hiện việc này. Làm thế nào có thể C + + làm điều này?

+2

@SanthoshPai Tôi không đồng ý với bạn. Cuộc gọi thứ hai chỉ thành công nếu các biến được khai báo trước cuộc gọi hàm và đây không phải là những gì OP đang yêu cầu – Amadeus

+0

Nếu bạn tạo các hình cầu "không sử dụng" bằng những tên đó, [Mã của bạn biên dịch như là] (http: //coliru.stacked -crooked.com/view?id=a1124762016375ec2b82bea6eed4622b-c96156d6cc95286981b0e9deef2eefae). Tôi không nghĩ rằng đó là một ý tưởng tốt mặc dù, đặc biệt là cho C + +. –

Trả lời

8

C++ không hỗ trợ nguyên bản này, vì vậy bạn không thể làm điều đó với bất kỳ chức năng hiện có nào cũ. Nếu bạn đang tạo API của riêng mình, bạn có thể sử dụng những gì được gọi là Named Parameter Idiom để mô phỏng nó. Ví dụ từ liên kết:

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

Bạn cũng có thể bao gồm API hiện tại với một cái gì đó tương tự như thế này. – fscan

+1

Có vẻ tốt hơn, nhưng điều này sẽ dẫn đến nhiều phương pháp công khai hơn, phá hủy đóng gói. – Yuanhang

+2

@Yuanhang: Không, nó không dẫn đến nhiều phương pháp công khai hơn. Nhìn lại, các phương thức nằm trên đối tượng 'OpenFile', nơi mà anh ta tạo một đối tượng' File'. Đối tượng 'File' không có phương thức công khai bổ sung nào khác ngoài phương thức khởi tạo chuyển đổi mới. –

5

Điều này là không thể trong C hoặc C++.

Tôi hiểu nỗi đau của bạn với điều này. Cá nhân tôi nghĩ rằng đó là một dấu hiệu của thiết kế xấu để có một chức năng chiếm hơn 9000 đối số, đặc biệt là nếu hầu hết trong số họ là NULL hoặc giá trị giữ chỗ. Nhiều chức năng chuẩn hóa POSIX ví dụ lấy một số loại struct tích lũy tất cả các giá trị cần thiết vào một đối số dễ hiểu.

+1

Có thể trong C++, nhưng cơ chế cơ bản có thể không đẹp. Bạn có thể khai báo hàm được gọi với tất cả các tham số như là một lớp Foo. Lớp Foo sẽ chứa một liên minh của tất cả các kiểu tham số có thể và một enum cho các tham số. Sau đó 'working_directory',' child_setup', et cetera có thể là các đối tượng toàn cục của các lớp với các định nghĩa của 'operator =', để 'working_directory = NULL' được đánh giá thành một cái gì đó ghi lại NULL trong union và đặt enum thành giá trị cho tham số 'working_directory'. Sau đó hàm được gọi phải giải mã các tham số được truyền cho nó. –

+1

@EricPostpischil: Trong khi đó chắc chắn sẽ hoạt động, nó thậm chí còn xấu hơn và dễ bị lỗi hơn một tấn thông số trên một hàm. – arne

+1

Nó hoàn toàn có thể trong C++, và thậm chí không khó, đặc biệt là khái niệm. (Nó mất rất nhiều mã phụ.Mặt khác, tôi chắc chắn sẽ đồng ý với bạn rằng thiết kế xấu của nó có chức năng chiếm hơn 9000 tham số, nhưng một số loại trợ giúp có thể hữu ích ngay cả đối với các chức năng mất ít hơn rất nhiều. Tôi chưa bao giờ thực sự nhìn thấy một hàm với 9000 tham số, nhưng tôi đã phải đối phó với một số trong mã kế thừa với 40 hoặc 50 tham số. –

4

Không, điều này không thể thực hiện được. Nhưng bạn có thể gán các giá trị NULL cho các biến và sau đó chuyển chúng thành các tham số nếu nó giúp với khả năng đọc của bạn!

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

Nhưng điều đó sẽ cần phải xác định các biến phụ và sẽ kém hiệu quả hơn nếu không có tối ưu hóa của trình biên dịch. – Yuanhang

+6

@Yuanhang: May mắn thay, trình biên dịch _do_ có tối ưu hóa! Hoan hô! –

+0

Chăm sóc để giải thích các downvote? – Paddyd

1

Chắc chắn là có thể. Nó thậm chí không đặc biệt khó khăn, nhưng nó liên quan đến rất nhiều mã. Một cái gì đó giống như sau đây có thể được sử dụng:

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; 
} 

Ghi chú:

  1. Tôi đã sử dụng C++ 11 đây. Điều tương tự cũng có thể được thực hiện trong phiên bản trước của C++, bằng cách thay thế các loại C trong union với unsigned char c[sizeof(C)];, thêm một cái gì đó cho union để đảm bảo sự liên kết chính xác (nếu cần), và rất nhiều loại đúc.

  2. này sẽ đơn giản hơn rất nhiều với boost::variant (thay vì các union) và boost::optional (trong MyFuncParamGroup). Tôi không có Boost, vì vậy tôi đã làm hầu hết những gì họ làm một cách rõ ràng. (Mà tất nhiên, làm cho mã dài hơn rất nhiều.)

  3. Tôi đã cố ý sử dụng hai tham số với cùng một loại, và người dùng xác định loại (C) với nhà thầu không tầm thường, để chứng tỏ những bị xử lý.

  4. Bạn có thể muốn đóng gói thêm một chút và kiểm tra lỗi hơn một chút .

Nhưng câu hỏi thực sự là: bạn có thực sự muốn đi tuyến đường này không? Số lượng mã tăng tuyến tính với số tham số . Và với bất kỳ trình soạn thảo phong nha nào, bạn có thể tạm thời đặt danh sách tham số từ khai báo hàm ở bên phải màn hình của bạn và điền thông số ở bên trái, trực tiếp dọc theo bên của khai báo tham số. (Trong gvim, tôi thường sẽ sử dụng chế độ chỉnh sửa khối cho điều này, nhưng cũng có thể được sử dụng .)

1

Các thông số được đặt tên rất hữu ích và tôi thậm chí còn xem xét bằng ngôn ngữ, đó là cách duy nhất để gọi một hàm ngoại trừ một tham số rõ ràng hiện tại.

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

C++ tiếc là không có chúng.

Quan trọng hơn C++ thiếu khả năng lập trình meta cần thiết để có thể triển khai chúng.

Trong khi có các thủ thuật có thể cố bắt chước các tham số có tên và ngay cả khi mã kết quả có vẻ gần như hợp lý, thì hoàn toàn không đứng đắn là mã bạn cần viết để có mô phỏng đó và không có cách nào trong C++ để tạo mã đó.

Điều tôi muốn nói là khi có thể viết một hàm chấp nhận các tham số có tên được mô phỏng hợp lý, mã cho chính hàm đó là ẩn và không thể được tạo tự động trong C++. Điều này có nghĩa là không ai sẽ viết các chức năng theo cách đó để tính năng này vẫn không có mặt. Tôi đoán rằng các thông số được đặt tên bị thiếu do sự hỗn hợp của sự thiếu hiểu biết (chúng tồn tại trước khi C++ được phát minh, có lẽ ngay cả trước C) và niềm tự hào (ngay cả trong C++ 11 khả năng lập trình ngôn ngữ là thảm hại và mọi thứ). tầm thường như nói liệt kê tại thời gian biên dịch các thành viên của một cấu trúc hoặc các tham số của một hàm là không thể).

Các vấn đề liên quan