0

How to pass parameters to a function by reference and through a pointer?

I have an example in which I try to call one of two write functions of the CFoo class. The example compiles if I specify two "parameter pack" const Args1... a, const Args0... b separated by commas, but if I specify the call OnFoo(&CFoo::write, ....); then it will only work for one of the two cases. And if I swap the "parameter pack" const Args1... a, const Args0... b then the picture will be completely opposite, the other call will work.

How can I make both calls work or replace the two "parameter pack" with one, so that I can pass parameters both by reference and through a pointer?

class CFoo
{
public:
    static void write(const char& a, const char& b, const char& c)
    {
    }

    static void write(const char* a, const char* b, const char* c)
    {
    }
};

#define TYPE_FUNCTION const Args1*... a, const Args0&... b // Work for call with reference
// #define TYPE_FUNCTION const Args0&... a, const Args1*... b // Work for call with point

template <class... Args0, class... Args1> void OnFoo(void (*func)(TYPE_FUNCTION), TYPE_FUNCTION)
{
}

int main(int argc, char** argv)
{
    OnFoo(&CFoo::write, '1', '1', '1');
    // OnFoo(&CFoo::write, "1", "1", "1");

    return 0;
}

First case

class CFoo
{
public:
    static void write(const char& a, const char& b, const char& c)
    {
    }

    static void write(const char* a, const char* b, const char* c)
    {
    }
};

#define TYPE_FUNCTION const Args1*... a, const Args0&... b // Work for call with reference

template <class... Args0, class... Args1> void OnFoo(void (*func)(TYPE_FUNCTION), TYPE_FUNCTION)
{
}

int main(int argc, char** argv)
{
    OnFoo(&CFoo::write, '1', '1', '1');
    OnFoo(&CFoo::write, "1", "1", "1"); // Error

    return 0;
}

Error:

main.cpp: In function 'int main(int, char**)':
main.cpp:24:38: error: no matching function for call to 'OnFoo(<unresolved overloaded function type>, const char [2], const char [2], const char [2])'
   24 |     OnFoo(&CFoo::write, "1", "1", "1");
      |                                      ^
main.cpp:17:48: note: candidate: 'template<class ... Args0, class ... Args1> void OnFoo(void (*)(const Args1* ..., const Args0& ...), const Args1* ..., const Args0& ...)'
   17 | template <class... Args0, class... Args1> void OnFoo(void (*func)(TYPE_FUNCTION), TYPE_FUNCTION)
      |                                                ^~~~~
main.cpp:17:48: note:   template argument deduction/substitution failed:
main.cpp:24:38: note:   mismatched types 'const Args0&' and 'const char*'
   24 |     OnFoo(&CFoo::write, "1", "1", "1");
      |                                      ^
main.cpp:24:38: note:   inconsistent parameter pack deduction with 'char' and 'char [2]'

Second case

class CFoo
{
public:
    static void write(const char& a, const char& b, const char& c)
    {
    }

    static void write(const char* a, const char* b, const char* c)
    {
    }
};

#define TYPE_FUNCTION const Args1&... a, const Args0*... b // Work for call with reference

template <class... Args0, class... Args1> void OnFoo(void (*func)(TYPE_FUNCTION), TYPE_FUNCTION)
{
}

int main(int argc, char** argv)
{
    OnFoo(&CFoo::write, '1', '1', '1'); // Error
    OnFoo(&CFoo::write, "1", "1", "1");

    return 0;
}

Error:

main.cpp:23:42: error: no matching function for call to 'OnFoo(<unresolved overloaded function type>, char, char, char)'
   23 |         OnFoo(&CFoo::write, '1', '1', '1');
      |                                          ^
main.cpp:17:52: note: candidate: 'template<class ... Args0, class ... Args1> void OnFoo(void (*)(const Args1& ..., const Args0* ...), const Args1& ..., const Args0* ...)'
   17 |     template <class... Args0, class... Args1> void OnFoo(void (*func)(TYPE_FUNCTION), TYPE_FUNCTION)
      |                                                    ^~~~~
main.cpp:17:52: note:   template argument deduction/substitution failed:
main.cpp:23:42: note:   mismatched types 'const Args0*' and 'const char&'
   23 |         OnFoo(&CFoo::write, '1', '1', '1');
      |                                          ^
main.cpp:23:42: note:   mismatched types 'const Args0*' and 'char'

The difference between the first and second cases is that I swapped the "parameter pack" which is passed by reference and via a pointer.

4
  • the macro is unecessary obsusfaction. You can use a type alias to make function pointer more readable Commented Nov 13, 2024 at 12:55
  • everything might look obvious to you, but for anybody else this is much simpler if you post the code that is not working and the compiler error message Commented Nov 13, 2024 at 12:56
  • what standard you're targeting? your OnFoo is essentially std::invoke, you can "steal" design from there.. or you can use it. Commented Nov 13, 2024 at 13:02
  • Unrelated, but if OnFoo is already a function template, why not make the callable also a template parameter rather than forcing it to be a function pointer? Commented Nov 14, 2024 at 13:23

2 Answers 2

2

You cannot pass overload set as parameter.

So function parameter should be non-deducible,

Then you might have function parameter depending of the other parameters, something like:

template <class... Args>
void OnFoo(std::conditional_t<(std::is_pointer_v<std::decay_t<Args>> && ...),
                              void(*)(std::decay_t<Args>...),
                            void(*)(const Args&...)>,
            Args&&...)
{
    // ...
}

Demo

Sign up to request clarification or add additional context in comments.

4 Comments

In the demo, would it be advisable to use std::forward when calling fct ?
Expected types are pointer or reference, so std::forward is unneeded here. But for more real use case, it might be advisable to use it. As I prefer your simplification from OP example, whereas a more complex way as mine might be required.
This is a cool solution. And it works. Thanks. I will mark it as correct. But it is my mistake. Firstly, I do not really know the standards since c++20, secondly, I did not say that I am programming for microcontrollers and I am also limited in standards (I do not even have std::function). So if you have another simpler solution, and if it exists, I would be grateful if you write it.
traits used can be reimplemented. but edrezen's simplification might be more appropriate.
2

You can do the following where you use the type information of ARGS in order to find the required overload version of CFoo::write:

#include <iostream>

class CFoo
{
public:
    static void write (char a, char b, char c)
    {
        std::cout << "version1: " << a << " " << b << " " << c << "\n";
    }

    static void write (const char* a, const char* b, const char* c)
    {
        std::cout << "version2: " << a << " " << b << " " << c << "\n";
    }
};

template <typename...ARGS> 
void OnFoo (void (*fct)(ARGS...), ARGS...args)
{
    fct (args...);
}

int main (int argc, char** argv)
{
     OnFoo (&CFoo::write, '1',  '2',  '3' );
     OnFoo (&CFoo::write, "11", "22", "33");

    return 0;
}

2 Comments

Fine, but you changed signature from const char& to char (probably fine for char, but not necessary for other types).
@Jarod42, yes indeed since using const& for simple types as char may be overkill here. But more generally, you're right and decaying when needed may be a better option as I have just seen in your solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.