Skip to content

Application interface. API and API Responses interface. #1

@Alex-Tsvetanov

Description

@Alex-Tsvetanov

Example for a structure of Application class with methods for setting up routes for APIs and Views.

#include <iostream>
#include <future>
#include <type_traits>
#include <string>
#include <string_view>
#include <vector>
#include <tuple>
#include <functional>
#include <algorithm>
#include <array>

// FixedString for NTTP string literals
template <size_t N>
struct FixedString {
    char value[N];
    constexpr FixedString(const char(&str)[N]) {
        std::copy_n(str, N, value);
    }
    constexpr operator std::string_view() const {
        return std::string_view(value);
    }
};

// Compile-time id generator
template<size_t Id>
struct counter {
    using tag = counter;

    struct generator {
        friend constexpr bool is_defined(tag) // The line that might cause a warning
        {
            return true;
        }
    };
    friend constexpr bool is_defined(tag);

    template<typename Tag = tag, bool = is_defined(Tag{}) >
    static constexpr bool exists(auto)
    {
        return true;
    }

    static constexpr bool exists(...)
    {
        return generator(), false;
    }
};

template<size_t Id = 0, typename = decltype([] {}) >
constexpr size_t unique_id() {
    if constexpr (not counter<Id>::exists(Id)) return Id;
    else return unique_id < Id + 1, decltype([] {}) > ();
}

// ---------- API Definition ----------
template <FixedString name, typename F>
struct ApiResponse;

template <size_t app_id, FixedString name, typename F>
struct ApiResponseRegister;

template <FixedString name, typename FRet, typename... FArgs>
struct ApiResponse<name, FRet(FArgs...)> {
    static constexpr auto name_template = name;
    static constexpr std::string_view name_str = name;
    using Ret = FRet;
    using Args = std::tuple<FArgs...>;
    ApiResponse() {}
    virtual std::future<FRet> get_future(FArgs...) const = 0;
};

template <size_t app_id, FixedString name, typename FRet, typename... FArgs>
struct ApiResponseRegister<app_id, name, FRet(FArgs...)> : public ApiResponse<name, FRet(FArgs...)> {
    static std::function<FRet(FArgs...)> func;
    std::future<FRet> future;
    ApiResponseRegister() : ApiResponse<name, FRet(FArgs...)>() {}
    virtual std::future<FRet> get_future(FArgs... args) const override
    {
        return std::async(func, args...);
    }
};

template <size_t app_id, FixedString name, typename FRet, typename... FArgs>
std::function<FRet(FArgs...)> ApiResponseRegister<app_id, name, FRet(FArgs...)>::func;

// ---------- Application Class ----------
template <size_t _id = unique_id<size_t{}, decltype([] {})>()>
class Application {
private:
    // Store API routes
    std::unordered_map<std::string, std::function<void()>> api_routes;
    // Store view routes
    std::unordered_map<std::string, std::function<void()>> view_routes;
public:
    Application() noexcept : api_routes(), view_routes()
    {

    }
    Application& operator=(const Application&) = delete;
    std::string render(std::string file, std::vector<std::string> params)
    {
        return " Hello World! " + file + " " + std::to_string(params.size());
    }
    template<FixedString name, typename Ret, typename... Args>
    Application<_id>& route_api(std::function<Ret(Args...)> func)
    {
        ApiResponseRegister<_id, name, Ret(Args...)>::func = func;
        return *this;
    }
    template<FixedString name, typename Lambda>
    Application<_id>& route_api(Lambda func)
    {
        return route_api<name>(std::function(func));
    }
    Application<_id>& route_view(std::string_view name, auto&& func)
    {
        return *this;
    }
};

template<size_t id = unique_id<size_t{}, decltype([] {})>()>
auto CreateApp = Application<id>{};

// ---------- Example Usage ----------

class UserData {
public:
    UserData(std::string username) : username(username) {}
    std::string getUsername() const { return username; }
private:
    std::string username;
};

int main() {
    Application<> app;
    app
        .route_api<"flight">([&]() -> void
        {
            int x;
            std::cin >> x;
            std::this_thread::sleep_for(std::chrono::seconds(5));
            std::cout << "text" << sizeof(app) << std::endl;
            std::cout << "text" << x << std::endl;
        })
        .route_api<"flight">([&]() -> void
        {
            std::cout << "haha" << std::endl;
        })
        .route_api<"getUserData">([&](std::string username)
        {
            return UserData{ username };
        })
        .route_view("/flight", [&](const ApiResponse<"flight", void()>& flightGetter)
        {
            std::cout << "text" << sizeof(app) << std::endl;
            std::cout << "waiting for API Response" << std::endl;
            flightGetter.get_future().wait();
            return app.render("index.html", std::vector<std::string>{ "text1", "text2" });
        });

    const ApiResponse<"flight", void()>& var = *(new ApiResponseRegister<0, "flight", void()>());
    var.get_future().wait();
    delete& var;
    return 0;
}
  • APIs are generic methods that might or might not accept parameters, do certain operations (business logic) and might or might not respond/return with data
  • ApiResponse class is a wrapper for APIs' methods that enables asynchronous call to the API in order to retrieve the wanted data
  • Views are meant to be platform-specific:
    • rendering HTML based on the data when it comes to Web Application
    • rendering XAML with ModelView containing the data when it comes to Mobile/Desktop Application (using .NET MAUI)

Metadata

Metadata

Labels

enhancementNew feature or request
No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions