The following information is for the latest version of the C SDK. If you are running an older version, please check the CloudBees Feature Management - C/C++ changelog for any differences. |
rox_ prefix functions
All rox_
prefix functions provide an interface for CloudBees Feature Management to manage feature flags that control the behavior of your application. They manage your application’s flag repository, handle communications with the server to obtain the latest flag values, implement flag settings, and set up flag configurations. The repository manages RoxStringBase
structures, which holds your application’s feature flags. Those variants display in the CloudBees Feature Management UI once the application is run.
The rox_
prefix functions also allow you to manage custom properties. These can be static settings of type string, boolean, integer, or double. Alternatively, you can use a generator function to provide a custom property that is dependent upon code state.
You can also use the RoxOptions
struct to configure some aspects of feature flag management in CloudBees Feature Management. For example, you can set the custom platform, impression handler, and fetch handler.
Rox
rox_setup
RoxStateCode rox_setup(const char *api_key, RoxOptions *options)
Configures the Rox object to work with the provided application.
Parameter | Modifier and Type | Description |
---|---|---|
apiKey |
const char * |
The environment key provided in the dashboard |
options |
RoxOptions |
A |
Return value
typedef enum RoxStateCode { RoxUninitialized = 0, RoxSettingUp = 1, RoxInitialized = 2, RoxShuttingDown = 3, RoxErrorEmptyApiKey = -1, RoxErrorInvalidApiKey = -2, RoxErrorGenericSetupFailure = -1000 } RoxStateCode
In case of a successful call, rox_setup
will return RoxInitialized
code. Otherwise, the application logs should be analyzed for errors and warnings. If rox_setup
returned failure code, it may be called again after fixing the errors from the logs.
Register
Registering feature flags is done by rox_add_flag
for boolean flags and
rox_add_variant
for variants.
rox_add_flag
RoxStringBase *rox_add_flag(const char *name, bool default_value)
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The flag name |
default_value |
bool |
The flag default value: true/false |
rox_add_string_with_options
RoxStringBase *rox_add_string(const char *name, const char *default_value) RoxStringBase *rox_add_string_with_options(const char *name, const char *default_value, RoxList *options)
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The variant name |
default_value |
const char * |
The variant default value |
options |
RoxList * |
a list of all possible values for the defined |
RoxStringBase *rox_add_int(const char *name, int default_value) RoxStringBase *rox_add_int_with_options(const char *name, int default_value, RoxList *options)
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The variant name |
default_value |
int |
The variant default value |
options |
RoxList * |
a list of all possible values for the defined |
RoxStringBase *rox_add_double(const char *name, double default_value) RoxStringBase *rox_add_double_with_options(const char *name, double default_value, RoxList *options)
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The variant name |
default_value |
double |
The variant default value |
options |
RoxList * |
a list of all possible values for the defined |
Example
RoxStringBase *videoChat = rox_add_flag("demo.video_chat", false); RoxStringBase *fontName = rox_add_string("fontName", "Arial"); RoxStringBase *titleColor = rox_add_string_with_options("titleColors", "red", ROX_LIST_COPY_STR("red", "blue", "green")); RoxStringBase *titleSize = rox_add_int_with_options("titleSize", 14, ROX_INT_LIST(14, 18, 24)); RoxStringBase *priceVarient = rox_add_double_with_options("price", 19.99, ROX_DBL_LIST(19.99, 29.99, 40.5));
Flags and variants registered to the dashboard will have the names
demo.video_chat
, fontName
, titleSize
, and titleColors
.
If you don’t need a namespace, you don’t have to specify it (like in
titleColors
example).
Custom Properties
The role of the Custom Properties is to segment the audience and apply a set of flags for target group defined by these properties.
The Custom Properties can be these types:
-
bool
-
int
-
double
-
semver
-
string
There are simple (direct) custom properties and calculated ones, using
RoxContext
to be more dynamic.
Simple (direct) custom properties
rox_set_custom_boolean_property
void rox_set_custom_boolean_property(const char *name, bool value)
Sets a custom property representing a boolean value.
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The name of the property to create |
value |
bool |
The value for the custom property: true/false |
rox_set_custom_string_property
void rox_set_custom_string_property(const char *name, const char *value)
Sets a custom property representing a string value.
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The name of the property to create |
value |
const char * |
The value for the custom property |
rox_set_custom_double_property
void rox_set_custom_double_property(const char *name, double value)
Sets a custom property representing a double value.
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The name of the property to create |
value |
double |
The value for the custom property |
rox_set_custom_integer_property
void rox_set_custom_integer_property(const char *name, int value)
Sets a custom property representing an int value.
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The name of the property to create |
value |
int |
The value for the custom property |
rox_set_custom_semver_property
void rox_set_custom_semver_property(const char *name, const char *value)
Sets a custom property representing a semver value.
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The name of the property to create |
value |
const char * |
The value for the custom property |
Computed custom properties (using RoxContext)
rox_set_custom_computed_boolean_property
void rox_set_custom_computed_boolean_property(const char *name, void *target, rox_custom_property_value_generator generator)
Sets a computed boolean Custom Property on the Rox client. This is a
computable boolean, with a function that generates the value for the
property. The generator should be of type
rox_custom_property_value_generator
.
Parameter | Modifier and Type | Description |
---|---|---|
name |
const char * |
The name of the property to create |
target |
void * |
any pointer, will be forwarded to the generator |
generator |
rox_custom_property_value_generator |
A rox_custom_property_value_generator type function |
rox_set_custom_computed_string_property
void rox_set_custom_computed_string_property(const char *name, void *target, rox_custom_property_value_generator generator)
Sets a computed string Custom Property on the Rox client. This is a
computable string, with a function that generates the value for the
property. The generator should be a delegate of type
rox_custom_property_value_generator
.
Parameter | Modifier and Type | Description |
---|---|---|
name |
string |
The name of the property to create |
target |
void * |
any pointer, will be forwarded to the generator |
generator |
rox_custom_property_value_generator |
A rox_custom_property_value_generator type function |
rox_set_custom_computed_integer_property
void rox_set_custom_computed_integer_property(const char *name, void *target, rox_custom_property_value_generator generator)
Sets a computed integer Custom Property on the Rox client. This is a
computable int, with a function that generates the value for the
property. The generator should be a delegate of type
rox_custom_property_value_generator
.
Parameter | Modifier and Type | Description |
---|---|---|
name |
string |
The name of the property to create |
target |
void * |
any pointer, will be forwarded to the generator |
generator |
rox_custom_property_value_generator |
A rox_custom_property_value_generator type function |
rox_set_custom_computed_semver_property
void rox_set_custom_computed_semver_property(const char *name, void *target, rox_custom_property_value_generator generator)
Sets a computed semantic-versioned string Custom Property on the Rox
client. This is a computable semantically-versioned string, with a
function that generates the value for the property. The generator should
be a delegate of type rox_custom_property_value_generator
.
Parameter | Modifier and Type | Description |
---|---|---|
name |
string |
The name of the property to create |
target |
void * |
any pointer, will be forwarded to the generator |
generator |
rox_custom_property_value_generator |
A rox_custom_property_value_generator type function |
rox_set_custom_computed_double_property
void rox_set_custom_computed_double_property( const char *name, void *target, rox_custom_property_value_generator generator)
Sets a computed double Custom Property on the Rox client. This is a
computable double, with a function that generates the value for the
property. The generator should be a delegate of type
rox_custom_property_value_generator
.
Parameter | Modifier and Type | Description |
---|---|---|
name |
string |
The name of the property to create |
target |
void * |
any pointer, will be forwarded to the generator |
generator |
rox_custom_property_value_generator |
A rox_custom_property_value_generator type function |
RoxOptions
RoxOptions
cover configuration options for the Rox client. These
include settings like configuration fetch interval, impression handler
and more.
Instances of this struct should be created using rox_options_create
.
Here is an example of setting up a new RoxOptions
struct. This options
object sets the version, provides an impression handler, and includes a fetch handler.
void rox_impression_h( void *target, RoxReportingValue *value, RoxExperiment *experiment, RoxContext *context) { // do something on impression } } RoxDynamicValue *dynamic_rule_h(const char *propName, void *target, RoxContext *context) { // return a value from the context, for ex: return rox_context_get(context, propName); } void test_configuration_fetched_h(void *target, RoxConfigurationFetchedArgs *args) { // do something when new configurations were set } } int main(int argc, char **argv) { RoxOptions *options = rox_options_create(); rox_options_set_configuration_fetched_handler(options, NULL, &configuration_fetched_h); rox_options_set_dev_mode_key(options, DEFAULT_DEV_MODE_KEY); rox_options_set_dynamic_properties_rule(options, NULL, &dynamic_rule_h); rox_options_set_impression_handler(options, NULL, &rox_impression_h); rox_options_set_roxy_url(options, "http://localhost:4444"); rox_options_set_version(options, "2.1.0"); rox_options_set_fetch_interval(options, 100); rox_setup("ROLLOUT_KEY", options); rox_shutdown(); }
rox_options_set_version
void rox_options_set_version(RoxOptions *options, const char *version)
Sets the version of the service running CloudBees Feature Management SDK. This can be used on the dashboard for targeting filtering.
rox_options_set_fetch_interval
void rox_options_set_fetch_interval(RoxOptions *options, int fetch_interval)
Sets the polling interval for fetching configuration from the CloudBees Feature Management storage service.
rox_options_set_configuration_fetched_handler
void rox_options_set_configuration_fetched_handler(RoxOptions *options, void *target, rox_configuration_fetched_handler handler)
Sets the configuration event handler to add actions after configurations are fetched.
rox_options_set_impression_handler
void rox_options_set_impression_handler(RoxOptions *options, void *target, rox_impression_handler handler)
Sets the impression event handler to add actions after a flag evaluation.
rox_options_set_dynamic_properties_rule
void rox_options_set_dynamic_properties_rule(RoxOptions *options, void *target, rox_dynamic_properties_rule rule)
DynamicPropertiesRule
is a:
RoxDynamicValue *(*rox_dynamic_properties_rule)(const char *prop_name, void *target, RoxContext *context)
The Dynamic Custom Property Generator is called when an explicit Custom Property definition does not exist on the client side.
If you do not set the rox_options_set_dynamic_properties_rule
it will
activate the default function, which tries to extract the value (by calling the name
of the flag from context).
if (context != NULL) { return rox_context_get(context, prop_name); } return NULL;
rox_options_set_roxy_url
void rox_options_set_roxy_url(RoxOptions *options, const char *roxy_url)
A roxy URL (see Microservices automated testing and local development).
RoxStringBase
RoxStringBase
is a feature flag struct. It represents a more complex flag type,
which includes a list of all possible values and a default value (rox_add_string
, rox_add_int
, rox_add_double
).
This list of values will be used when selecting new values for the feature and will
be available for override via the dashboard.
rox_is_enabled
bool rox_is_enabled(RoxStringBase *variant)
Returns the feature flag’s current value (or the default if no value was set).
rox_is_enabled_ctx
bool rox_is_enabled_ctx(RoxStringBase *variant, RoxContext *context)
Returns the feature flag current value (or the default if no value was set) considering the given context.
rox_get_xxx
char *rox_get_string(RoxStringBase *variant) int rox_get_int(RoxStringBase *variant) double rox_get_double(RoxStringBase *variant)
Returns the variant flag’s current value (or the default if no value was set).
rox_get_xxx_ctx
char *rox_get_string_ctx(RoxStringBase *variant, RoxContext *context) int rox_get_int_ctx(RoxStringBase *variant, RoxContext *context) double rox_get_double_ctx(RoxStringBase *variant, RoxContext *context)
Returns the variant current value (or the default if no value was set) considering the given context.
RoxContext
The context struct is used to pass data to the flag or variant evaluation. This struct is used by the registered Custom Properties to evaluate the experiment expression and return the value.
You can create a context using the rox_context_create_empty
, sending a
dictionary to its Build
function.
rox_context_create_from_map
RoxContext *rox_context_create_from_map(RoxMap *map)
Creates a RoxContext
from a RoxMap
(see below).
Keys are strings. Values are of RoxDynamicValue
type.
rox_context_get
RoxDynamicValue *rox_context_get(RoxContext *context, const char *key)
Returns a value in the context (can be used in a set_computed__custom__…_property
handler). See RoxDynamicValue
).
rox_context_free
void rox_context_free(RoxContext *context)
Frees the memory of the context after use is done.
Example
// create a generator for a custom property RoxDynamicValue *get_user_id(void *target, RoxContext *context) { // getting the user id from the context used return rox_context_get(context, "userId"); } int main() { // set custom property rox_set_custom_computed_string_property"id_property", NULL, &get_user_id); // creating a flag RoxStringBase *specialDiscount = rox_add_flag("billing.isSpecialDiscount", false); // creating context RoxContext *userContext = rox_context_create_from_map( ROX_MAP(ROX_COPY("userId"), rox_dynamic_value_create_int(555))); // evaluate a flag, using context bool isDiscounted = rox_is_enabled_ctx(specialDiscount, userContext); rox_context_free(userContext); }
Using ConfigurationFetchedHandler
As seen in RoxOptions
, rox_options_set_configuration_fetched_handler
can
be used to set ConfigurationFetchedHandler
. This is how the handler
looks:
typedef void (*rox_configuration_fetched_handler)(void *target, RoxConfigurationFetchedArgs *args)
The additional definitions used are:
RoxConfigurationFetchedArgs
typedef struct RoxConfigurationFetchedArgs { RoxFetchStatus fetcher_status; const char *creation_date; bool has_changes; RoxFetcherError error_details; } RoxConfigurationFetchedArgs
The struct the configuration fetched handler gets:
RoxFetchStatus
typedef enum RoxFetchStatus { AppliedFromEmbedded = 1, AppliedFromLocalStorage, AppliedFromNetwork, ErrorFetchedFailed } RoxFetchStatus
Enum represents the fetch source.
RoxFetcherError
typedef enum RoxFetcherError { NoError = 0, CorruptedJson = 1, EmptyJson, SignatureVerificationError, NetworkError, MismatchAppKey, UnknownError } RoxFetcherError
In case the fetch failed, an Enum represents the error type.
For an example, see Configuration fetched handler.
Using ImpressionHandler
The impression Handler delegate/function (as seen in RoxOptions
rox_options_set_impression_handler
) has a couple of useful
parameters which can help you decide on further actions.
the handle signature is:
typedef void (*rox_impression_handler)(void *target, RoxReportingValue *value, RoxExperiment *experiment, RoxContext *context)
and the relevant additional definitions:
RoxReportingValue
typedef struct RoxReportingValue {const char *name; const char *value; } RoxReportingValue
The Impression flag name and the evaluated value.
RoxExperiment
typedef struct RoxExperiment {char *name; char *identifier; bool archived; RoxSet *labels; char *stickiness_property; } RoxExperiment
The relevant flag’s experiment.
For an example, see Impression handler.
RoxDynamicValue
RoxDynamicValue
represents a dynamic value, which wraps a type value.
Mostly used when working with RoxContext
and custom properties.
rox_dynamic_value_create_int
RoxDynamicValue *rox_dynamic_value_create_int(int value)
Wrapping an int value into a RoxDynamicValue
.
rox_dynamic_value_create_double
RoxDynamicValue *rox_dynamic_value_create_double(double value)
Wrapping a double value into a RoxDynamicValue
.
rox_dynamic_value_create_double_ptr
RoxDynamicValue *rox_dynamic_value_create_double_ptr(double *value)
Wrapping a double pointer value into a RoxDynamicValue.
rox_dynamic_value_create_boolean
RoxDynamicValue *rox_dynamic_value_create_boolean(bool value)
Wrapping a bool value into a RoxDynamicValue
.
rox_dynamic_value_create_string_copy
RoxDynamicValue *rox_dynamic_value_create_string_copy(const char *value)
Creating a RoxDynamicValue
by copying the value that has to be freed after use.
rox_dynamic_value_create_string_ptr
RoxDynamicValue *rox_dynamic_value_create_string_ptr(char *value)
Wrapping a string value into a RoxDynamicValue
.
String will be freed by calling dynamic_value_free()
.
rox_dynamic_value_create_list
RoxDynamicValue *rox_dynamic_value_create_list(RoxList *value)
Wrapping a RoxList
value into a RoxDynamicValue
.
List will be freed by calling dynamic_value_free()
.
rox_dynamic_value_create_map
RoxDynamicValue *rox_dynamic_value_create_map(RoxMap *value)
Wrapping a RoxMap
value into a RoxDynamicValue
Map will be freed by calling dynamic_value_free()
.
rox_dynamic_value_create_map
RoxDynamicValue *rox_dynamic_value_create_null()
Creating a null value.
rox_dynamic_value_create_copy
RoxDynamicValue *rox_dynamic_value_create_copy(RoxDynamicValue *value)
Creating a copy of a RoxDynamicValue.
rox_dynamic_value_free
RoxDynamicValue *rox_dynamic_value_free(RoxDynamicValue *value)
Frees the RoxDynamicValue
.
Should be called after rox_context_get
.
rox_dynamic_value_is_int
bool rox_dynamic_value_is_int(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is of type int.
rox_dynamic_value_is_double
bool rox_dynamic_value_is_double(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is of type double.
rox_dynamic_value_is_boolean
bool rox_dynamic_value_is_boolean(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is of type bool.
rox_dynamic_value_is_string
bool rox_dynamic_value_is_string(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is of type string.
rox_dynamic_value_is_list
bool rox_dynamic_value_is_list(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is of type RoxList
.
rox_dynamic_value_is_map
bool rox_dynamic_value_is_map(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is of type RoxMap
.
rox_dynamic_value_is_undefined
bool rox_dynamic_value_is_undefined(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is undefined.
rox_dynamic_value_is_null
bool rox_dynamic_value_is_null(RoxDynamicValue *value)
Returns true if a RoxDynamicValue
is null.
rox_dynamic_value_get_int
int rox_dynamic_value_get_int(RoxDynamicValue *value)
Returns the int value of the RoxDynamicValue
.
rox_dynamic_value_get_double
double rox_dynamic_value_get_double(RoxDynamicValue *value)
Returns the double value of the RoxDynamicValue
.
rox_dynamic_value_get_boolean
bool rox_dynamic_value_get_boolean(RoxDynamicValue *value)
Returns the bool value of the RoxDynamicValue
.
rox_dynamic_value_get_string
char *rox_dynamic_value_get_string(RoxDynamicValue *value)
Returns the string value of the RoxDynamicValue
.
DynamicApi
DynamicApi
is a way to evaluate flags and variant by name on the fly,
without creating it before.
rox_dynamic_api
RoxDynamicApi *rox_dynamic_api()
Create a RoxDynamicApi
, the proxy to all dynamic flags and variants
evaluation.
rox_dynamic_api_is_enabled
bool rox_dynamic_api_is_enabled(RoxDynamicApi *api, const char *name, bool default_value, RoxContext *context)
Evaluate a flag by its name and context.
If no experiment was set via the dashboard, the default_value
will be
returned.
rox_dynamic_api_get_xxx(_ctx)
char *rox_dynamic_api_get_string(RoxDynamicApi *api, const char *name, char *default_value) char *rox_dynamic_api_get_string_ctx(RoxDynamicApi *api, const char *name, char *default_value, RoxList *options, RoxContext *context) int rox_dynamic_api_get_int(RoxDynamicApi *api, const char *name, int default_value) int rox_dynamic_api_get_int_ctx(RoxDynamicApi *api, const char *name, int default_value, RoxList *options, RoxContext *context) double rox_dynamic_api_get_double(RoxDynamicApi *api, const char *name, double default_value) double rox_dynamic_api_get_double_ctx(RoxDynamicApi *api, const char *name, double default_value, RoxList *options, RoxContext *context)
Evaluate a flag by its name and context.
Options will be sent to the dashboard to allow an easy pick in the experiment.
If no experiment was set via the dashboard, the default_value
will be
returned.
rox_dynamic_api_free
void rox_dynamic_api_free(RoxDynamicApi *api)
Frees the RoxDynamicApi
that was created with rox_dynamic_api
.
For an example, see Dynamic API.
Logging
Logging helps debug in case something behave unexpectedly.
To set up CloudBees Feature Management Logging use rox_logging_init
.
If no logging was set, the default settings will be used, meaning printing to stdout errors level logs.
rox_logging_init
void rox_logging_init(RoxLoggingConfig *config)
Sets up a logging with RoxLoggingConfig
.
RoxLoggingConfig
typedef struct RoxLoggingConfig {RoxLogLevel min_level; void *target; rox_logging_handler handler; bool print_time; } RoxLoggingConfig
Sets up logging configuration.
Setting up a rox_logging_handler
will override the default handler that uses the other properties.
Configuration can be created using ROX_LOGGING_CONFIG_INITIALIZER
, setting the log_level
, and using default for all other properties.
RoxLogLevel
typedef enum RoxLogLevel { RoxLogLevelTrace = 1, RoxLogLevelDebug, RoxLogLevelWarning, RoxLogLevelError, RoxLogLevelNone } RoxLogLevel
rox_logging_handler
typedef void (*rox_logging_handler)(void *target, RoxLogMessage *message)
Sets logging handler to handle the logging messages yourself.
RoxLogMessage
typedef struct RoxLogMessage { const char *file; int line; RoxLogLevel level; const char *level_name; const char *message; } RoxLogMessage
For an example, see Turning on verbose logging.
Helpers
Functions
ROX_LIST_COPY_STR
#define ROX_LIST_COPY_STR(...) rox_list_create_str_va(NULL, __VA_ARGS__, NULL)
Create a RoxList
from strings, creating a copy for each string.
ROX_INT_LIST
#define ROX_INT_LIST(...) rox_list_create_int_va(NULL, __VA_ARGS__, INT_MIN)
Create a RoxList
from integers.