Home > Software engineering >  calling class function based on variadic function arguments resulting an error
calling class function based on variadic function arguments resulting an error

Time:11-22

Facing some compilation errors when using the below example with a variable number of arguments & need some help to find some solution or some better approach.

I am trying to initialize the class variables based on input arguments supplied below criteria, like in the below example I am willing to initialize the "data" class methods "select_area1_object" & "select_area2_object" in which arguments are variable but I am willing to create some common way in which from main I just need to pass the type(which is area code in this example or any other input parameter) & arguments of it, from the type value passed from main class should decide which function to call and based on that it should initialize its variable number of arguments, please see below example.

#include <iostream>
#include <functional>
using namespace std;

enum area {AREA_1, AREA_2, AREA_3};

class data {
    public :
        int number;
        std::string name;
        std::string address;
        int area_code;
    
    template <typename type, typename... args>
    void create_object(type t, args&&... arg)
    {
        // here area1 or are2 methods should be called based on type & it should process variable arguments supplied from main.
        if(t == AREA_1)
        {
            select_area1_object(arg...);    
        }
        else if(t == AREA_2)
        {
            select_area2_object(arg...);
        }
    }
    
    void select_area1_object(int num, std::string nm, std::string ad)
    {
        number = num;
        name = nm;
        address = ad;
    }
    void select_area2_object(int num, std::string nm)
    {
        number = num;
        name = nm;
    }
};

// trying to create a common template here so that from the main I will not have to create an object and only need to worry about passing output arguments (like number, name, address).
template <typename type, typename... args>
void request_process(type t, args&&... arg)
{
    data d;
    d.create_object(t, std::forward<args>(arg)...);
}

int main()
{
    request_process(1, 1, "area1", "area1_address");
    request_process(2, 2, "area2");
    std::cout << "End of program" << endl;
}

seeing below errors :

tmp/858Sn1cFdf.cpp: In instantiation of 'void data::create_object(type, args&& ...) [with type = int; args = {int, const char (&)[6], const char (&)[14]}]':
/tmp/858Sn1cFdf.cpp:45:5:   required from 'void request_process(type, args&& ...) [with type = int; args = {int, const char (&)[6], const char (&)[14]}]'
/tmp/858Sn1cFdf.cpp:50:51:   required from here
/tmp/858Sn1cFdf.cpp:24:13: error: no matching function for call to 'data::select_area2_object(int&, const char [6], const char [14])'
   24 |             select_area2_object(arg...);
      |             ^~~~~~~~~~~~~~~~~~~
/tmp/858Sn1cFdf.cpp:34:10: note: candidate: 'void data::select_area2_object(int, std::string)'
   34 |     void select_area2_object(int num, std::string nm)
      |          ^~~~~~~~~~~~~~~~~~~
/tmp/858Sn1cFdf.cpp:34:10: note:   candidate expects 2 arguments, 3 provided
/tmp/858Sn1cFdf.cpp: In instantiation of 'void data::create_object(type, args&& ...) [with type = int; args = {int, const char (&)[6]}]':
/tmp/858Sn1cFdf.cpp:45:5:   required from 'void request_process(type, args&& ...) [with type = int; args = {int, const char (&)[6]}]'
/tmp/858Sn1cFdf.cpp:51:34:   required from here
/tmp/858Sn1cFdf.cpp:20:13: error: no matching function for call to 'data::select_area1_object(int&, const char [6])'
   20 |             select_area1_object(arg...);
      |             ^~~~~~~~~~~~~~~~~~~
/tmp/858Sn1cFdf.cpp:28:10: note: candidate: 'void data::select_area1_object(int, std::string, std::string)'
   28 |     void select_area1_object(int num, std::string nm, std::string ad)
      |          ^~~~~~~~~~~~~~~~~~~
/tmp/858Sn1cFdf.cpp:28:10: note:   **candidate expects 3 arguments, 2 provided**

If I remove the AREA2 functionality from the class like its method, conditional check & calling from main "request_process(2, 2, "area2");" it's working fine, so it's not accepting variable arguments I think, the same thing tried with other project but same argument error is seen, and I am willing to write some more functionality based on this in which arguments will vary & do not want to create class object in main & let template handle class object creation and initialization of class method.

Please suggest to me if this has already some solution provided as I did not find similar problems described.

CodePudding user response:

The function calls in both braches of the if and else should be valid(see the comments in the code below). This is because template arguments to args are known at compile time while the function argument to arg are passed at runtime.

To make that possible, you need to add one overload for each of the shown functions as shown below:

class data {
    public :
     //other code here as before

    //added this overfload with 2 parameters only
    void select_area1_object(int num, std::string nm)
    {
        //code here
    }
    void select_area1_object(int num, std::string nm, std::string ad)
    {
        //code here
    }
    void select_area2_object(int num, std::string nm)
    {
        //code here
    }
    //added this overload with 3 parameters
    void select_area2_object(int num, std::string nm, std::string ad)
    {
        //code here
    }
};

Working demo

CodePudding user response:

This has nothing to do with variable arguments. Types in C simply don't work this way.

If you have if (condition) { statements1 } else { statements2 } then both branches of the if must be type-correct.

Your construction is not very much different from this:

if (false) 
   std::cout << std::cos("hello, world");

If this doesn't work, then yours cannot work either. And this doesn't work.

One possible solution is to unify the interfaces for select_area1_object, select_area2_object, and any other select_areaN_object you might come up with in the future. You might make them all accept the same std::variant, check at run time if the variant passed in is the variant they expect, and complain and error out if not.

The branches of this std::variant could be tuples of arguments that your original select_areaN_object expect.

Another solution is to make t a non-type template parameter instead of a normal function parameter, and do if constexpr instead of the regular if.

  • Related