-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
Public API review for SignalR C++ Client #8717
Comments
Couple notes from PR reviews: |
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
@BrennanConroy are these issues still relevant or are we tracking the client work separately? |
hub_connection_builder.hnamespace signalr
{
class hub_connection_builder
{
public:
hub_connection_builder(std::string url);
~hub_connection_builder();
hub_connection_builder(const hub_connection_builder&) = delete;
hub_connection_builder(hub_connection_builder&&) noexcept;
hub_connection_builder& operator=(hub_connection_builder&&) noexcept;
hub_connection_builder& operator=(const hub_connection_builder&) = delete;
hub_connection_builder& configure_options(signalr_client_config config);
hub_connection_builder& with_logging(std::shared_ptr<log_writer> logger, trace_level log_level);
hub_connection_builder& with_websocket_factory(std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory);
hub_connection_builder& with_http_client_factory(std::function<std::shared_ptr<http_client>(const signalr_client_config&)> http_client_factory);
hub_connection_builder& skip_negotiation(bool skip = true);
hub_connection_builder& with_messagepack_hub_protocol();
std::unique_ptr<hub_connection> build();
}
} Usage auto hub_connection = std::shared_ptr<signalr::hub_connection>(
signalr::hub_connection_builder("http://localhost:5000/hub")
.skip_negotiation()
.build()); Talking points:
hub_connection.hnamespace signalr
{
class hub_connection
{
public:
~hub_connection();
hub_connection(const hub_connection&) = delete;
hub_connection& operator=(const hub_connection&) = delete;
hub_connection(hub_connection&&) noexcept;
hub_connection& operator=(hub_connection&&) noexcept;
void start(std::function<void(std::exception_ptr)> callback) noexcept;
void stop(std::function<void(std::exception_ptr)> callback) noexcept;
connection_state get_connection_state() const;
const std::string& get_connection_id() const;
void on_disconnected(std::function<void (std::exception_ptr)> disconnected_callback);
void on(const std::string& method_name, std::function<void (const std::vector<signalr::value>&)> handler);
void invoke(const std::string& method_name, const std::vector<signalr::value>& arguments = std::vector<signalr::value>(), std::function<void(const signalr::value&, std::exception_ptr)> callback = [](const signalr::value&, std::exception_ptr) {}) noexcept;
void send(const std::string& method_name, const std::vector<signalr::value>& arguments = std::vector<signalr::value>(), std::function<void(std::exception_ptr)> callback = [](std::exception_ptr) {}) noexcept;
}
} Usage: std::shared_ptr<hub_connection> connection;
connection->on("Receive", [](const std::vector<signalr::value>& args)
{
args[0].as_double();
args[1].as_map();
});
connection->on_disconnected([](std::exception_ptr exception)
{
std::cout << "Connection closed" << std::endl;
});
connection->start([](std::exception_ptr exception)
{
if (exception != std::nullptr)
{
try
{
std::rethrow_exception(exception);
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
}
});
connection->invoke("Send", std::vector<signalr::value> { "Test", 10 }, [](const signalr::value& args, std::exception_ptr exception)
{
args[0].as_string();
});
connection->stop([](std::exception_ptr exception)
{
});
signalr_value.hnamespace signalr
{
enum class value_type
{
map,
array,
string,
float64,
null,
boolean,
binary
};
class value
{
public:
value();
value(std::nullptr_t);
value(value_type t);
value(bool val);
value(double val);
value(const std::string& val);
value(std::string&& val);
value(const char* val);
value(const char* val, size_t length);
value(const std::vector<value>& val);
value(std::vector<value>&& val);
value(const std::map<std::string, value>& map);
value(std::map<std::string, value>&& map);
value(const std::vector<uint8_t>& bin);
value(std::vector<uint8_t>&& bin);
value(const value& rhs);
value(value&& rhs) noexcept;
~value();
value& operator=(const value& rhs);
value& operator=(value&& rhs) noexcept;
bool is_map() const;
bool is_double() const;
bool is_string() const;
bool is_null() const;
bool is_array() const;
bool is_bool() const;
bool is_binary() const;
double as_double() const;
bool as_bool() const;
const std::string& as_string() const;
const std::vector<value>& as_array() const;
const std::map<std::string, value>& as_map() const;
const std::vector<uint8_t>& as_binary() const;
value_type type() const;
}
}
log_writer.hnamespace signalr
{
class log_writer
{
public:
// NOTE: the caller does not enforce thread safety of this call
virtual void write(const std::string &entry) = 0;
virtual ~log_writer() {}
};
} Usage: hub_connection_builder("").with_logging(std::make_shared<my_logger>(), trace_level::debug);
class my_logger : public log_writer
{
public:
virtual void write(const std::string& entry) override
{
std::cout << entry << std::endl;
}
} trace_level.hnamespace signalr
{
enum class trace_level : int
{
verbose = 0,
debug = 1,
info = 2,
warning = 3,
error = 4,
critical = 5,
none = 6,
};
}
signalr_client_config.hnamespace signalr
{
class signalr_client_config
{
public:
signalr_client_config();
const std::map<std::string, std::string>& get_http_headers() const noexcept;
void set_http_headers(const std::map<std::string, std::string>& http_headers);
void set_scheduler(std::shared_ptr<scheduler> scheduler);
std::shared_ptr<scheduler> get_scheduler() const noexcept;
void set_handshake_timeout(std::chrono::milliseconds);
std::chrono::milliseconds get_handshake_timeout() const noexcept;
void set_server_timeout(std::chrono::milliseconds);
std::chrono::milliseconds get_server_timeout() const noexcept;
void set_keepalive_interval(std::chrono::milliseconds);
std::chrono::milliseconds get_keepalive_interval() const noexcept;
}
}
http_client.hnamespace signalr
{
enum class http_method
{
GET,
POST
};
// created by signalr and passed to http_client impls
class http_request final
{
public:
http_request()
: method(http_method::GET), timeout(std::chrono::seconds(120))
{ }
http_method get_method() const;
const std::map<std::string, std::string>& get_headers() const;
const std::string& get_content() const;
std::chrono::seconds get_timeout() const;
};
// created by http_client impls, consumed by signalr
class http_response final
{
public:
http_response(); // default 200 status code?
http_response(int code, const std::string& content);
http_response(int code, std::string&& content);
http_response(http_response&& rhs);
http_response(const http_response& rhs);
http_response& operator=(const http_response& rhs);
http_response& operator=(http_response&& rhs) noexcept;
};
class http_client
{
public:
virtual void send(const std::string& url, http_request& request,
std::function<void(const http_response&, std::exception_ptr)> callback, cancellation_token token) = 0;
virtual ~http_client() {}
};
}
websocket_client.hnamespace signalr
{
class websocket_client
{
public:
virtual ~websocket_client() {};
virtual void start(const std::string& url, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) = 0;
virtual void stop(std::function<void(std::exception_ptr)> callback) = 0;
virtual void send(const std::string& payload, std::function<void(std::exception_ptr)> callback) = 0;
virtual void receive(std::function<void(const std::string&, std::exception_ptr)> callback) = 0;
};
}
transfer_format.hnamespace signalr
{
enum class transfer_format
{
text,
binary
};
}
connection_state.hnamespace signalr
{
enum class connection_state
{
connecting,
connected,
disconnected
};
} scheduler.hnamespace signalr
{
typedef std::function<void()> signalr_base_cb;
struct scheduler
{
virtual void schedule(const signalr_base_cb& cb, std::chrono::milliseconds delay = std::chrono::milliseconds::zero()) = 0;
virtual ~scheduler() {}
};
} cancellation_token.hnamespace signalr
{
class cancellation_token_source;
class cancellation_token
{
public:
void register_callback(std::function<void()> callback);
bool is_canceled() const;
};
}
transport_type.hnamespace signalr
{
enum class transport_type
{
websockets
};
}
|
API Review Notes:
TBC |
I would instead return
Yes. This improves optimization opportunities. I'd also recommend referring to https://github.com/isocpp/CppCoreGuidelines. Taking some time to audit this API and how it works with the GSL might also be worthwhile. |
Wouldn't this make It would make it harder (impossible) to create a reference cycle via lambda captures (std::shared_ptr) though which is nice. |
Based on the statement, "We think pointer is fine. You can create a shared_ptr if you want.", there is an expectation for users to take a raw pointer and create a There should be no reason to use raw pointers at an API level in C++14. In C++11 there are a few cases, but none jumped out to me in the above API. Working through some use cases with the GSL would help with some of this. |
Ah, that's helpful.
This seems difficult since the repo says it assumes C++14 or higher... we're stuck targeting C++11 |
Oh, I missed that. Sigh... Reviewing the core guidelines is worth at least an hour or two though. |
API Review Notes (Cont.):
|
API Review Notes:
|
Thanks for contacting us. We're moving this issue to the |
Thanks for contacting us. We're moving this issue to the |
Epic #5301
We need to do a public API review for the SignalR C++ client to make sure we're happy with it.
The text was updated successfully, but these errors were encountered: