c++optimizationstructparameter-passinglong-parameter-list

How to make the parameter list of a function cleaner?


In a video that I recently watched, Kate Gregory suggests that if a function has a huge parameter list (e.g. 4+ parameters) one should put all those parameters in a struct (i.e. make them members of the struct) and after initializing an instance of the struct, pass it to the said function (obviously this requires refactoring the function itself too).

I have a utility function with 5 parameters which you can see below:

bool convert_str_to_valid_ints( const std::string_view inputStr, const std::span<int> result_ints,
                                const std::size_t expectedTokenCount, const std::vector<int>& specificTokensIndices,
                                const std::pair<int, int> acceptableRange =
                                std::pair<int, int>( std::numeric_limits<int>::min( ), std::numeric_limits<int>::max( ) ) );

As can be expected, calling this function in any call site introduces a massive and scary piece of code!

The above can become this:

// In a header file
struct ParameterList
{
    std::string_view inputStr;
    std::span<int> result_ints;
    std::size_t expectedTokenCount;
    std::vector<int> specificTokensIndices;
    std::pair<int, int> acceptableRange = std::pair<int, int>( std::numeric_limits<int>::min( ),
                                                               std::numeric_limits<int>::max( ) );
};

bool convert_str_to_valid_ints( const ParameterList& pl );
.
.
.
// in any source file that includes the said header file
const ParameterList pl { /* arguments go here */ };

bool isValid { convert_str_to_valid_ints( pl ) }; // much more readable, and possibly more performant?

I have tried to break it into smaller pieces (e.g. two smaller functions each with fewer parameters) but I have failed to do so since what this fucntion does can not logically be broken into two pieces and it doesn't make much sense. So I want to ask for help to see whether Gregory's method will be a valid choice in this particular case or not.

Now if one says it's a valid choice, then where should I put the definition of this special purpose struct (my guess is inside the header in which the big function is declared so that other source files can access the struct by including the big function's header and then call the funtion)?


Solution

  • The struct should be in a header file, probably on its own in its own header file. Have you considered this? Adding the convert function to the struct, so it can use the parameters directly, it also allows you to reuse them later.

    #include <iostream>
    #include <string_view>
    #include <span>
    #include <vector>
    
    struct ParameterListConverter
    {
        std::span<int> result_ints; // you can even set some reasonable defaults here
        std::size_t expectedTokenCount;
        std::vector<int> specificTokensIndices;
        std::pair<int, int> acceptableRange = std::pair<int, int>(std::numeric_limits<int>::min(),
        std::numeric_limits<int>::max());
    
        bool convert(const std::string input) const
        {
            // ... your convert function using the parameters set
        };
    };
    
    
    int main()
    {
        ParameterListConverter converter
        {
            .expectedTokenCount = 42ul
        };
    
        bool success = converter.convert("123,234");
    
        return 1;
    }