Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance "template metaprogramming magic" for HTTP methods #1117

Open
GitSparTV opened this issue Sep 30, 2024 · 2 comments · May be fixed by #1118
Open

Enhance "template metaprogramming magic" for HTTP methods #1117

GitSparTV opened this issue Sep 30, 2024 · 2 comments · May be fixed by #1118

Comments

@GitSparTV
Copy link

Is your feature request related to a problem?

Library provides good experience by providing variadic template interface in HTTP methods, however, you can't template-ly select HTTP method to call.

For example, you have myGet function:

template <class... Ts>
cpr::Response myGet(Ts&&... ts) {
    return cpr::Get(cpr::Bearer{"ACCESS_TOKEN"}, cpr::Header{{"Content-Type", "application/json; charset=utf-8"}});
}

It's great, the headers will always be in the request. However, you also need the same wrapper for POST method.
I can duplicate code, but it's lame.

The other way is to provide the function to myGet (ignore the name):

template <typename F, class... Ts>
cpr::Response myGet(F f, Ts&&... ts) {
    return f(std::forward<Ts>(ts)..., cpr::Bearer{"ACCESS_TOKEN"}, cpr::Header{{"Content-Type", "application/json; charset=utf-8"}});
}

The solution will not make you happy:

    cpr::Response r = myGet(cpr::Post<cpr::Url, cpr::Body, cpr::Bearer, cpr::Header>, cpr::Url{"http://www.httpbin.org/post?a=b"}, cpr::Body{"{\"a\": true}"});

You have to specify template parameters for cpr::Post and it looks worse than making a duplicate.

Possible Solution

Add HTTPMethod and a universal template to make a request.

#include <cpr/cpr.h>

namespace cpr {

enum class HTTPMethod {
    kGet,
    kHead,
    kPost,
    kPut,
    kDelete,
    kOptions,
    kPatch,
};

template <HTTPMethod method, typename... Ts>
Response Request(Ts&&... ts) {
    cpr::Session session;
    cpr::priv::set_option(session, std::forward<Ts>(ts)...);

    using enum HTTPMethod;

    if constexpr (method == kGet) {
        return session.Get();
    } else if constexpr (method == kHead) {
        return session.Head();
    } else if constexpr (method == kPost) {
        return session.Post();
    } else if constexpr (method == kPut) {
        return session.Put();
    } else if constexpr (method == kDelete) {
        return session.Delete();
    } else if constexpr (method == kOptions) {
        return session.Options();
    } else if constexpr (method == kPatch) {
        return session.Patch();
    } else {
        static_assert(method == kGet, "Unknown method");
    }
}

}

template <cpr::HTTPMethod method, class... Ts>
cpr::Response MyResponse(Ts&&... ts) {
    return Request<method>(std::forward<Ts>(ts)..., cpr::Bearer{"ACCESS_TOKEN"}, cpr::Header{{"Content-Type", "application/json; charset=utf-8"}});
}

int main() {
    cpr::Response r = MyResponse<cpr::HTTPMethod::kPost>(cpr::Url{"http://www.httpbin.org/post?a=b"}, cpr::Body{"{\"a\": true}"});

    std::cout << r.text << std::endl;
}

Alternatives

  • compile-time if
  • duplicated code for each method

Additional Context

No response

@COM8
Copy link
Member

COM8 commented Sep 30, 2024

@GitSparTV I like this approach. Would you like to create a PR for this?

@GitSparTV
Copy link
Author

@COM8

Sure

GitSparTV added a commit to GitSparTV/cpr that referenced this issue Sep 30, 2024
@GitSparTV GitSparTV linked a pull request Sep 30, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants