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

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions