Search code examples
c++c++11lambdaunique-ptrreturn-type

How to return the std::unique_ptr containing custom deleter in C++11?


My application compiler could only support up to c++11.

Below is a snip code of my project and function get_conn() returns std::unique_ptr with custom deleter (deleter needs two arguments). I'm using auto keyword for return type but it gives an error like if is compiled with c++11 (compiles fine with c++14)

error: ‘get_conn’ function uses ‘auto’ type specifier without trailing return type

Sample code for demonstration:

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


// Dummy definition of API calls                                                                                                             
int* open_conn (int handle)
{
  return new int;
}
void close_conn (int handle, int *conn)
{}

auto get_conn (int handle)
{
  // API call                                                                                                                                
  int* conn = open_conn (handle);    
  auto delete_conn = [](int *conn, int handle) {
        // API call                                                                                                                          
    close_conn (handle, conn);
    delete conn;
  };
  auto delete_binded = std::bind (delete_conn, std::placeholders::_1, handle);
  return std::unique_ptr<int, decltype(delete_binded)> (conn, delete_binded);
}

int main()
{
  int handle = 2; // suppose                                                                                                                 
  auto c = get_conn (handle);
  if (!c)
    cout << "Unable to open connection\n";

  return 0;
};

How can I replace auto keyword with actual return type of std::unique_ptr to compatible code with c++11 ?

I tried with below return type but failed

std::unique_ptr<int, void(*)(int *,int)> get_conn(int handle)
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
   // ...
}

Solution

  • Back to the functor!

    The auto return type for functions is feature. In order to provide the actual return type, you can provide a functor like follows(as @IgorTandetnik mentioned in the comments). The advantage is, that you do not need to std::bind any more.

    (See online)

    struct DeleteConn  // functor which subsituts the lambda and `std::bind`
    {
        int _handle;
        explicit DeleteConn(int handle): _handle{ handle } {}
        void operator()(int* conn) const
        {
            // API call                                                                                                                          
            close_conn(_handle, conn);
            delete conn;
        }
    };
    
    std::unique_ptr<int, DeleteConn> get_conn(int handle)
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---> now you can provide the actual type.
    {
        // API call                                                                                                                                
        int* conn = open_conn(handle);
        DeleteConn delete_conn{ handle };
        return std::unique_ptr<int, DeleteConn>(conn, delete_conn);
    }
    

    Alternatively, you can move the lambda function delete_conn out from the get_conn function and use the trailing return type which is a fetaure.

    (See Online)

    namespace inter {
        auto delete_conn = [](int* conn, int handle)
        {
            // API call                                                                                                                          
            close_conn(handle, conn);
            delete conn;
        };
    }
    
    auto get_conn(int handle) 
    ->std::unique_ptr<int, decltype(std::bind(inter::delete_conn, std::placeholders::_1, handle))>
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --->Trailing return type
    {
            // API call                                                                                                                                
            int* conn = open_conn(handle);
            auto delete_binded = std::bind(inter::delete_conn, std::placeholders::_1, handle);
            return std::unique_ptr<int, decltype(delete_binded)>(conn, delete_binded);
    }