0

I have questions regarding functions, more specific on function signatures and how to pass them. I might be a trivial or even stupid question, but I couldn't find a satisfying answer yet.

Please consider this example which uses a std::unique_ptr to manage a file pointer:

#include <iostream>
#include <memory>

void func1(FILE* f)
{
    std::cout << "func1 called" << std::endl;
    fclose(f);
}

int main() 
{
    FILE* f = fopen("testfile.txt", "w");
    if(f)
    {
        std::unique_ptr<FILE, void(*)(FILE*)> fptr(f, &func1);
    }

    return 0;
}

This kind of smart pointer needs a functions signature as second template argument (shared_ptr does not for some odd reason). My current understanding of interpreting the signature here is

void () (FILE) is a pointer to a function returning nothing (void) and a filepointer as argument.

As a result, the user has to pass the address of the desired function with the address operator. At this point, a few questions arise:

1.) Removal of the address operator works just as well, no compiler warning is thrown, code works. Shouldn't this be an error/warning?

2.) If I use a reference to a function (e.g. unique_ptr<FILE, void(&)(FILE*)>(f, func1) ) it works as expected, so is this a superior way to pass a function as it is unambiguous?

3.) Removing the middle specifier altogether (e.g. unique_ptr<FILE, void()(FILE*)>(f, func1) ) causes compiler errors, so is passing a function by value impossible in general? (if it is, then it would make sense to overload the version in 1. by implicitly converting the function to a function pointer)

DocDriven
  • 3,726
  • 6
  • 24
  • 53
  • `fopen` is not idiomatic C++ though - it's not a good idea to mix C-style with modern C++. – Dai Feb 08 '22 at 00:39
  • 1
    [`shared_ptr` is a weird beast](https://stackoverflow.com/questions/21355037/why-does-unique-ptr-take-two-template-parameters-when-shared-ptr-only-takes-one). Check out the question comments and the second answer in addition ot the chosen answer to get more scope. – user4581301 Feb 08 '22 at 00:40
  • 1
    You might want to learn c++ from a textbook instead of patchworking with SO. – Passer By Feb 08 '22 at 00:41
  • 1
    @Dai It's fine, it works and they're times when it's necessary. Particularly because it's used in conjunction with `unique_ptr`. – Passer By Feb 08 '22 at 00:43
  • **(1)** You observe a [function-to-pointer implicit conversion](https://en.cppreference.com/w/cpp/language/implicit_conversion#Function_to_pointer). **(2)** What do you mean "unambiguous"? In what way are other approaches "ambiguous"? **(3)** `void()(FILE*)` attempts to declares a function that takes no parameters and returns a function `void(FILE*)` - but a function cannot return another function. It's unclear what you are trying to achieve with this. – Igor Tandetnik Feb 08 '22 at 15:01

1 Answers1

1

Function name is the always address of function (pointer to the function). If you want to use something else you can use some wrapper, e.g. std::function:

#include <iostream>
#include <memory>
#include <functional>

class Closer {
public:
    void operator ()(FILE *f) {
        std::cout << "func1 called" << std::endl;
        fclose(f);
    }
};

int main() 
{
    FILE* f = fopen("testfile.txt", "w");
    if(f)
    {
        std::unique_ptr<FILE, std::function<void(FILE*)>> fptr(f, Closer());
    }

    return 0;
}

Lambda in that case is anonymous functional object.