Determine whether function parameter is a function

2020-07-30 03:49发布

问题:

How can I determine whether a function's parameter type is a function? I'm implementing a class called Queue which receives a single parameter. If the parameter is a function, it stores the function.

Here is the code:

template <class Type, typename Data>
class Queue {
    public:
        void Enqueue (Data& data) {
            if (typeid(data).name() == int) {
                intVector.push_back(data);
                order.push_back("int");
            }
            else if (typeid(data).name() == bool) {
                boolVector.push_back(data);
                order.push_back("bool");
            }
            else if (typeid().name() == string) {  
              stringVector.push_back(data);
              order.push_back("string");
            }
            // This will continue for:
            // - double
            // - char
            // - function
        }

        auto Dequeue () {
            auto temp;
            switch (order.begin()) {
                case "int":
                    temp = intVector.begin();
                    intVector.erase(intVector.begin());
                    order.erase(order.begin());
                    return temp;
                // This will continue for:
                // - "string"
                // - "bool"
                // - "char"
                // - "double"
                // - "function"
                default:
                    cout << "An Error occurred while trying to Enqueue." << endl;
                    cout << "\tAddress: " << this << endl;
            }
        }

        auto Start () {
            // This function will run all of the processes...
        }
        Queue (Data& data) {
            if (typeid(Type).name() == int) {
                // Pseodo-code:
                // if (data.type == function) {
                //     Enqueue (data);
                // }
            }
        }
}

It can be initialised:

Queue queue1 = new Queue <int> (func ()); // func () is a function.
Queue queue2 = new Queue <int> (var);     // var is a variable.

回答1:

Oh my. This is a bit of an XY problem.

Anyway, after messing around with std::enable_if for a bit (which was kinda fun), I realised that the whole thing can be boiled down to this:

#include <vector>
#include <string>
#include <any>
#include <iostream>
#include <functional>

void call_if_function (void (* f) ()) { f (); }
void call_if_function (std::function <void ()> f) { f (); }
void call_if_function (std::any x) { (void) x; }

template <class T>
class Queue
{
    public:
        void Enqueue (const T& data)
        {
//          std::cout << "Enqueueing " << data << "\n";
            v.push_back (data);
        }

        T Dequeue ()
        {
            T ret = v.front ();
//          std::cout << "Dequeueing " << ret << "\n";
            v.erase (v.begin ());
            call_if_function (ret);
            return ret;
        }

    private:
        std::vector <T> v;
};

And, if I understand the OP's problem right, that is all all you need.

Test program:

void foo () { std::cout << "foo () called\n"; }
void bar (int x, int y) { std::cout << "bar () called, x = " << x << ", y = " << y << "\n"; }

int main ()
{
    // Queue of int's
    Queue <int> int_q;
    int_q.Enqueue (42);
    auto i = int_q.Dequeue ();
    std::cout << "int_q.Dequeue () returned " << i << "\n\n";

    // Queue of strings
    Queue <std::string> string_q;
    string_q.Enqueue ("Hello world");
    auto s = string_q.Dequeue ();
    std::cout << "string_q.Dequeue () returned " << s << "\n\n";

    // Call function with no parameters    
    Queue <void (*)()> func_q;
    func_q.Enqueue (foo);
    auto f = func_q.Dequeue ();
    std::cout << "func_q.Dequeue () returned " << (void *) f << "\n";
    f ();

    // Call function with arbitrary parameters
    Queue <std::function <void ()>> func_qp;
    func_qp.Enqueue ([] () { bar (21, 99); });
    auto fp = func_qp.Dequeue ();
    fp ();
}

Output:

int_q.Dequeue () returned 42

string_q.Dequeue () returned Hello world

foo () called
func_q.Dequeue () returned 0x4026fd
foo () called

bar () called, x = 21, y = 99
bar () called, x = 21, y = 99

Live demo.

Moral: KISS, there are far too many toys in the toybox these days. Enjoy the weekend people.



回答2:

And, since I took the time to research it a bit (mainly because I wanted to learn a bit about it), here is a bit of super-simple SFINAE cobbled together from the wise ones.

#include <type_traits>
#include <iostream>

// Primary template (required)
template <class T, class Enable = void>
struct X { };

// Specialisation to take a function pointer
template <class T>
struct X <T, typename std::enable_if <std::is_function<T>::value>::type>
{
    X (T func)
    {
        std::cout << "T is a function\n";
        func ();
    }
};

// Partial specialisation for anything else
template<class T>
struct X <T, typename std::enable_if <!std::is_function<T>::value>::type>
{
    X (T x)
    {
        std::cout << "T is not a function (and x is " << x << ")\n";
    }
};

void foo () { std::cout << "foo () called\n"; }

int main ()
{
    X <void ()> x1 (foo);
    X <int> x2 (42);
}

Output:

T is a function
foo () called
T is not a function (and x is 42)

Live demo.

Powerful stuff, but not the answer to every little problem.



回答3:

How can I determine whether a function's parameter type is a function?

You can use the std::is_function to do that.

An implementation like

template <class Type, typename Data>
class Queue {
    public:
        Queue (Data& data) {
            if (typeid(Type).name() == int) {
                // Pseodo-code:
                if (std::is_function<data.type>::value) {
                     Enqueue (data);
                }
            }
        }
}

won't work though, since the part inside the if block scope is seen for other data types by the compiler.

To realize that you'll need to use SFINAE, and provide different specializations of your Queue constructor function using std::enable_if.



回答4:

For a SFINAE example:

Queue(std::enable_if_t<std::is_function_v<Data>, Data>::type & data)
{
    //data is a function
}

//I'm not sure if the enable_if is needed here, maybe you can just do Data& data
Queue(std::enable_if_t<!std::is_function_v<Data>, Data>::type & data)
{
    //data is not a function
}

(You need to include <type_traits> to use std::is_function_v)



标签: c++ templates