split property & values into separate classes and files

Signed-off-by: Folling <mail@folling.io>
This commit is contained in:
folling 2023-11-21 15:10:43 +01:00 committed by Folling
parent f38ebeab14
commit e377340781
Signed by: folling
SSH key fingerprint: SHA256:S9qEx5WCFFLK49tE/LKnKuJYM5sw+++Dn6qJbbyxnCY
28 changed files with 700 additions and 269 deletions

View file

@ -1,12 +0,0 @@
This list is intended to help keep the documentation up to date.
If you make changes to, for example, templates, always check the documentation for templates.
But sometimes information is shared. and referenced in multiple places. This helps keep track of that.
Usage: Search for these keys prefixed with IMPLEMENTATION_DETAIL_* to change documentation in relevant places.
DATABASE: References to the usage of a database
TREE_LAYOUT: References to our usage of a tree layout
OBJECT_TYPES: References to the types of objects
OBJECT_SCOPES: References to the usage of object scopes
PROPERTY_TYPES: The property types that currently exist
LAZY_VALUE_CREATION: The fact that values are created lazily

View file

@ -77,7 +77,10 @@ IKA_API void ikarus_blueprint_get_linked_entities(
/// \pre \li Must exist.
/// \return The blueprint represented as an object or null if an error occurs.
/// \remark This operation is guaranteed to be very fast and is intended to be used frequently.
IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint const * blueprint);
IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint);
/// \see ikarus_blueprint_to_object
IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint);
IKARUS_END_HEADER

View file

@ -50,12 +50,7 @@ struct IkarusEntity;
/// \return The created entity or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API IkarusEntity * ikarus_entity_create(
struct IkarusProject * project,
struct IkarusEntityFolder * parent,
size_t position,
char const * name,
struct IkarusBlueprint ** blueprints,
size_t blueprints_count
struct IkarusProject * project, char const * name, struct IkarusBlueprint ** blueprints, size_t blueprints_count
);
/// \brief Deletes an entity.
@ -67,16 +62,6 @@ IKA_API void ikarus_entity_delete(IkarusEntity * entity);
IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint);
/// \brief Checks if an entity has a specific property.
/// \param entity The entity to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param property The property to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return True if the entity has the property, false otherwise.
IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property);
/// \brief Links an entity to a blueprint.
/// \param entity The entity to link.
/// \pre \li Must not be null.
@ -98,6 +83,16 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru
/// \remark No-op if the entity is not linked to the blueprint.
IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint);
/// \brief Checks if an entity has a specific property.
/// \param entity The entity to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param property The property to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return True if the entity has the property, false otherwise.
IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property);
/// \brief Gets the number of properties of an entity.
/// \param entity The entity to get the number of properties of.
/// \pre \li Must not be null.
@ -126,7 +121,7 @@ IKA_API void ikarus_entity_get_properties(
/// \pre \li Must exist.
/// \return The value of the property or null if the entity does not have the property or an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct IkarusProperty const * property);
IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property);
/// \brief Sets the value of a property of an entity.
/// \param entity The entity to set the value of.
@ -138,11 +133,10 @@ IKA_API struct IkarusEntityValue * get_value(IkarusEntity const * entity, struct
/// \param value The new value of the property.
/// \pre \li Must not be null.
/// \pre \li Must be of the same type as the property.
/// \param validate_settings If set, this function fails not only if the type of the value is invalid, but also if it's not
/// valid under the properties settings. \see property.h
/// \pre \li Must be valid for the property's settings.
/// \remark If the entity does not have the property, this function fails.
IKA_API void ikarus_entity_set_value(
IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue * value, bool validate_settings
IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value
);
/// \brief Casts an entity to an object.
@ -151,7 +145,10 @@ IKA_API void ikarus_entity_set_value(
/// \pre \li Must exist.
/// \return The entity represented as an object or null if an error occurs.
/// \remark This operation is guaranteed to be very fast and is intended to be used frequently.
IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity const * entity);
IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity);
/// \see ikarus_entity_to_object
IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity);
IKARUS_END_HEADER

View file

@ -41,6 +41,18 @@ IKA_API void ikarus_object_visit(
void * data
);
/// \see ikarus_object_visit
IKA_API void ikarus_object_visit_const(
IkarusObject const * object,
void (*blueprint_visitor)(struct IkarusBlueprint const *, void *),
void (*property_visitor)(struct IkarusProperty const *, void *),
void (*entity_visitor)(struct IkarusEntity const *, void *),
void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *),
void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *),
void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *),
void * data
);
IKARUS_END_HEADER
// @}

View file

@ -10,10 +10,7 @@
IKARUS_BEGIN_HEADER
// IMPLEMENTATION_DETAIL_OBJECT_TYPES
/// \brief The type of an object.
/// \remark The folder types are identical to their counterparts in #IkarusFolderType.
enum IkarusObjectType {
/// \brief Not an object or no object.
IkarusObjectType_None = 0,

View file

@ -0,0 +1,25 @@
#pragma once
/// \file number_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// \brief Number properties store a numeric value. (e.g. "Weight" or "Age")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusNumberProperty;
IKA_API IkarusNumberProperty * ikarus_number_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
struct IkarusNumberSettings * settings
);
IKARUS_END_HEADER
/// @}

View file

@ -1,12 +1,10 @@
#pragma once
// IMPLEMENTATION_DETAIL_LAZY_VALUE_CREATION
/// \file property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
#include <ikarus/objects/property_type.h>
#include <ikarus/objects/properties/property_type.h>
#include <ikarus/stdtypes.h>
/// \defgroup properties Properties
@ -52,33 +50,12 @@ IKARUS_BEGIN_HEADER
/// \remark Properties' tree structures are scoped to the entity or blueprint they are associated with.
struct IkarusProperty;
/// \brief Creates a property
/// \param project The project the property is part of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param property_source The property source the property is part of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The name of the property.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param property_info The info of the property.
/// \pre \li Must not be null.
/// \return The created property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusProperty * ikarus_property_create(
struct IkarusProject * project,
struct IkarusPropertySource * property_source,
char const * name,
struct IkarusPropertyTypeInfo * property_info
);
/// \brief Deletes a property.
/// \param property The property to delete.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \remark The property must not be accessed after deletion.
IKA_API void ikarus_property_delete(struct IkarusProperty * property);
IKA_API void ikarus_property_delete(IkarusProperty * property);
/// \brief Gets the type info of a property.
/// \param property The property to get the type info of.
@ -86,7 +63,18 @@ IKA_API void ikarus_property_delete(struct IkarusProperty * property);
/// \pre \li Must exist.
/// \return The type info of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProperty const * property);
IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property);
/// \brief Gets the settings of a property.
/// \param property The property to get the settings of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The settings of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusPropertySettings * ikarus_property_get_settings(IkarusProperty * property);
/// \see ikarus_property_get_settings
IKA_API struct IkarusPropertySettings const * ikarus_property_get_settings_const(IkarusProperty const * property);
/// \brief Gets the source of a property.
/// \param property The property to get the source of.
@ -94,25 +82,31 @@ IKA_API struct IkarusPropertyTypeInfo * ikarus_property_get_type_info(IkarusProp
/// \pre \li Must exist.
/// \return The source of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusPropertySource * ikarus_property_get_source(IkarusProperty const * property);
IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property);
/// \brief Gets the default value of a property.
/// \param property The property to get the default value of.
/// \brief Visits a property. Calling the appropriate function for the property's type.
/// \param property The property to visit.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The default value of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property);
/// \param toggle_property The function to call if the property is a toggle property. Skipped if null.
/// \param number_property The function to call if the property is a number property. Skipped if null.
/// \param text_property The function to call if the property is a text property. Skipped if null.
/// \param data The data to pass to the functions.
IKA_API void ikarus_property_visit(
IkarusProperty * property,
void (*toggle_property)(struct IkarusToggleProperty *, void *),
void (*number_property)(struct IkarusNumberProperty *, void *),
void (*text_property)(struct IkarusTextProperty *, void *),
void * data
);
/// \brief Sets the type info of a property and resets all values to the new default value.
/// \param property The property to set the type info of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param new_type The new type info of the property.
/// \param attempt_conversion Whether to attempt to convert the property's values to the new type info. Conversion rules are
/// unspecified for now, but follow common sense.
IKA_API void ikarus_property_set_type_info(
IkarusProperty * property, struct IkarusPropertyTypeInfo new_type_info, bool attempt_conversion
/// \see ikarus_property_visit
IKA_API void ikarus_property_visit_const(
IkarusProperty const * property,
void (*toggle_property)(struct IkarusToggleProperty const *, void *),
void (*number_property)(struct IkarusNumberProperty const *, void *),
void (*text_property)(struct IkarusTextProperty const *, void *),
void * data
);
/// \brief Casts a property to an object.
@ -121,7 +115,10 @@ IKA_API void ikarus_property_set_type_info(
/// \pre \li Must exist.
/// \return The property represented as an object or null if an error occurs.
/// \remark This operation is guaranteed to be very fast and is intended to be used frequently.
IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty const * property);
IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property);
/// \see ikarus_property_to_object
IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property);
IKARUS_END_HEADER

View file

@ -10,7 +10,7 @@
IKARUS_BEGIN_HEADER
struct PropertySource;
struct IkarusPropertySource;
/// \brief Creates an blueprint property source.
/// \param blueprint The blueprint to create the property source for.
@ -18,7 +18,7 @@ struct PropertySource;
/// \pre \li Must exist.
/// \return The created property source or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint);
IKA_API struct IkarusPropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint);
/// \brief Creates an entity property source.
/// \param entity The entity to create the property source for.
@ -26,7 +26,7 @@ IKA_API struct PropertySource * ikarus_property_source_create_blueprint(struct I
/// \pre \li Must exist.
/// \return The created property source or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct PropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity);
IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity);
/// \brief Visits a property source, calling the appropriate callback.
/// \param property_source The property source to visit.
@ -36,12 +36,20 @@ IKA_API struct PropertySource * ikarus_property_source_create_entity(struct Ikar
/// \param entity_visitor The callback to call if the source is an entity, skipped if null.
/// \param user_data User data to pass to the callbacks.
IKA_API void ikarus_property_source_visit(
struct PropertySource * property_source,
struct IkarusPropertySource * property_source,
void (*blueprint_visitor)(struct IkarusBlueprint *, void *),
void (*entity_visitor)(struct IkarusEntity *, void *),
void * user_data
);
/// \see ikarus_property_source_visit
IKA_API void ikarus_property_source_visit_const(
struct IkarusPropertySource const * property_source,
void (*blueprint_visitor)(struct IkarusBlueprint const *, void *),
void (*entity_visitor)(struct IkarusEntity const *, void *),
void * user_data
);
IKARUS_END_HEADER
/// @}

View file

@ -1,7 +1,5 @@
#pragma once
// IMPLEMENTATION_DETAIL_PROPERTY_TYPES
/// \file property_type.h
/// \author Folling <folling@ikarus.world>

View file

@ -0,0 +1,31 @@
#pragma once
/// \file number_property_settings.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup property_settings PropertySettings
/// \brief Number property settings add additional constraints to number properties.
/// \details The following settings are available:
///
/// @{
IKARUS_BEGIN_HEADER
struct IkarusNumberPropertySettings;
/// \brief Sets the default value for a number property.
/// \param settings The number property settings.
/// \pre \li Must not be null.
/// \param default_value The default value.
/// \pre \li Must not be null.
/// \pre \li Must be a valid value for the property.
/// \remark The settings take ownership of the value, the caller must not free it.
IKA_API void ikarus_number_property_settings_set_default_value(
struct IkarusNumberPropertySettings * settings, struct IkarusNumberValue * default_value
);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,92 @@
#pragma once
/// \file property_settings.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \defgroup property_settings PropertySettings
/// \brief Property settings add additional constraints to properties.
/// \details Each property has a certain set of settings. The options available depend on the type of the property.
/// Settings can be changed after the property has been created. Examples of settings are:
/// Note that the default value must be set using the concrete subtypes to ascertain type correctness.
/// - Minimum: The minimum value for a number property.
/// - Matches: A regular expression that the value of a text property must match.
/// There are also some common settings, shared among all properties:
/// - Default Value (default: PropertyType's default default (sic) value): The value that is returned if no value is specified
/// for some entity.
/// - List (default: false): If set to true, the property becomes a list. Instead of one number, you could then specify a series
/// of numbers. Note that each element in the list is subject to changes in values. E.g. when we say that all values are changed
/// in some way (e.g. reset to the default value), this applies to all elements in the list.
/// - Allow undefined (default: false): If set to true, you can specify an "unknown" value for a property.
/// It might not be known if a character is dead or not for example.
/// @{
IKARUS_BEGIN_HEADER
struct IkarusPropertySettings;
/// \brief Gets the default value of a property.
/// \param settings The settings to get the default value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The default value of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusValue const * ikarus_property_settings_get_default_value(IkarusPropertySettings const * settings);
/// \brief Fetches whether a property is a list.
/// \param settings The property settings.
/// \pre \li Must not be null.
/// \return True if the property is a list, false otherwise.
IKA_API bool ikarus_property_settings_is_list(IkarusPropertySettings const * settings);
/// \brief Sets whether a property is a list.
/// \details Noop if unchanged. A change from list -> single truncates to just the first element. A change from single -> list
/// sets the first element to the current value.
/// \param settings The property settings.
/// \pre \li Must not be null.
/// \param list Whether the property should be
/// a list.
IKA_API void ikarus_property_settings_set_is_list(IkarusPropertySettings * settings, bool list);
/// \brief Fetches whether a property may be undefined.
/// \param settings The property settings.
/// \pre \li Must not be null.
/// \return True if the property may be undefined, false otherwise.
IKA_API bool ikarus_property_settings_may_be_undefined(IkarusPropertySettings const * settings);
/// \brief Sets whether a property may be undefined.
/// \details Noop if unchanged. If the transition is from undefined -> defined, all undefined values will be reset to the
/// default value.
/// \param settings The property settings.
/// \pre \li Must not be null.
/// \param allow_undefined Whether the property should be allowed to be undefined.
/// \param allow_undefined Whether the property should be allowed to be undefined.
IKA_API void ikarus_property_settings_set_may_be_undefined(IkarusPropertySettings * settings, bool allow_undefined);
/// \brief Visits a property settings. Calling the appropriate function for the property's type.
/// \param settings The property settings.
/// \pre \li Must not be null.
/// \param toggle_property_visitor The function to call if the property is a toggle property. Skipped if null.
/// \param number_property_visitor The function to call if the property is a number property. Skipped if null.
/// \param text_property_visitor The function to call if the property is a text property. Skipped if null.
/// \param data Data passed to the visitors.
IKA_API void ikarus_property_settings_visit(
struct IkarusPropertySettings * settings,
void (*toggle_property_visitor)(struct IkarusTogglePropertySettings * settings, void * data),
void (*number_property_visitor)(struct IkarusNumberPropertySettings * settings, void * data),
void (*text_property_visitor)(struct IkarusTextPropertySettings * settings, void * data),
void * data
);
IKA_API void ikarus_property_settings_visit_const(
struct IkarusPropertySettings const * settings,
void (*toggle_property_visitor)(struct IkarusTogglePropertySettings const * settings, void * data),
void (*number_property_visitor)(struct IkarusNumberPropertySettings const * settings, void * data),
void (*text_property_visitor)(struct IkarusTextPropertySettings const * settings, void * data),
void * data
);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,31 @@
#pragma once
/// \file text_property_settings.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup property_settings PropertySettings
/// \brief Text property settings add additional constraints to text properties.
/// \details The following settings are available:
///
/// @{
IKARUS_BEGIN_HEADER
struct IkarusTextPropertySettings;
/// \brief Sets the default value for a text property.
/// \param settings The text property settings.
/// \pre \li Must not be null.
/// \param default_value The default value.
/// \pre \li Must not be null.
/// \pre \li Must be a valid value for the property.
/// \remark The settings take ownership of the value, the caller must not free it.
IKA_API void ikarus_text_property_settings_set_default_value(
struct IkarusTextPropertySettings * settings, struct IkarusTextValue * default_value
);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,31 @@
#pragma once
/// \file toggle_property_settings.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup property_settings PropertySettings
/// \brief Toggle property settings add additional constraints to toggle properties.
/// \details The following settings are available:
///
/// @{
IKARUS_BEGIN_HEADER
struct IkarusTogglePropertySettings;
/// \brief Sets the default value for a toggle property.
/// \param settings The toggle property settings.
/// \pre \li Must not be null.
/// \param default_value The default value.
/// \pre \li Must not be null.
/// \pre \li Must be a valid value for the property.
/// \remark The settings take ownership of the value, the caller must not free it.
IKA_API void ikarus_toggle_property_settings_set_default_value(
struct IkarusTogglePropertySettings * settings, struct IkarusToggleValue * default_value
);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,25 @@
#pragma once
/// \file text_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// \brief Text properties store an arbitrary piece of text. (e.g. "Firstname" or "Description")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusTextProperty;
IKA_API IkarusTextProperty * ikarus_text_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
struct IkarusTextSettings * settings
);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,25 @@
#pragma once
/// \file toggle_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// \brief Toggle properties store a value that can be either true or false. (e.g. "Is the character dead?")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusToggleProperty;
IKA_API IkarusToggleProperty * ikarus_toggle_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
struct IkarusToggleSettings * settings
);
IKARUS_END_HEADER
/// @}

View file

@ -1,79 +0,0 @@
#pragma once
/// \file property_info.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// @{
IKARUS_BEGIN_HEADER
/// \brief Information about a property.
/// \details Property information includes their type and settings consolidated to ascertain type safety.
struct IkarusPropertyTypeInfo;
/// \brief Information about a toggle property.
struct IkarusTogglePropertyInfo;
/// \brief Information about a number property.
struct IkarusNumberPropertyInfo;
/// \brief Information about a text property.
struct IkarusTextPropertyInfo;
/// \brief Creates a new toggle property info.
/// \return The created toggle property info.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTogglePropertyInfo * ikarus_toggle_property_info_create();
/// \brief Sets the default value of a toggle property info.
/// \param toggle_property_info The toggle property info to set the default value of.
/// \pre \li Must not be null.
/// \param default_value The default value to set.
/// \pre \li Must not be null.
IKA_API void ikarus_toggle_property_info_set_default_value(
IkarusTogglePropertyInfo * toggle_property_info, struct IkarusToggleValue * default_value
);
/// \brief Converts a toggle property info to a generic property info.
/// \param toggle_property_info The toggle property info to convert.
/// \return The converted property info.
IKA_API IkarusPropertyTypeInfo * ikarus_toggle_property_info_to_property_info(IkarusTogglePropertyInfo * toggle_property_info);
/// \brief Creates a new number property info.
/// \return The created number property info.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberPropertyInfo * ikarus_number_property_info_create();
/// \brief Sets the default value of a number property info.
/// \param number_property_info The number property info to set the default value of.
/// \pre \li Must not be null.
/// \param default_value The default value to set.
/// \pre \li Must not be null.
IKA_API void ikarus_number_property_info_set_default_value(
IkarusNumberPropertyInfo * number_property_info, struct IkarusNumberValue * default_value
);
/// \brief Converts a number property info to a generic property info.
/// \param number_property_info The number property info to convert.
/// \return The converted property info.
IKA_API IkarusPropertyTypeInfo * ikarus_number_property_info_to_property_info(IkarusNumberPropertyInfo * number_property_info);
/// \brief Creates a new text property info.
/// \return The created text property info.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextPropertyInfo * ikarus_text_property_info_create();
/// \brief Sets the default value of a text property info.
/// \param text_property_info The text property info to set the default value of.
/// \pre \li Must not be null.
/// \param default_value The default value to set.
/// \pre \li Must not be null.
IKA_API void ikarus_text_property_info_set_default_value(
IkarusTextPropertyInfo * text_property_info, struct IkarusTextValue * default_value
);
/// \brief Converts a text property info to a generic property info.
/// \param text_property_info The text property info to convert.
/// \return The converted property info.
IKA_API IkarusPropertyTypeInfo * ikarus_text_property_info_to_property_info(IkarusTextPropertyInfo * text_property_info);
IKARUS_END_HEADER
/// @}

View file

@ -0,0 +1,44 @@
#pragma once
/// \file number_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup entity_value Entity Values
/// @{
IKARUS_BEGIN_HEADER
/// \brief A true/false boolean-like value. For example "IsDead".
struct IkarusNumberValue;
/// \brief Creates a number value from a long double.
/// \param value The numeric value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberValue * ikarus_number_value_create(long double value);
/// \brief Creates an indeterminate number value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate();
/// \brief Sets the value of a number value.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param new_value The new value.
IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value);
/// \brief Fetches the underlying value of a number value.
/// \param value The number value.
/// \return The underlying value.
/// \warning If the value is indeterminate, false is returned.
IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value);
/// \brief Converts a number value to an entity value.
/// \param number_value The number value to convert.
/// \return The converted entity value.
IKA_API struct IkarusEntityValue * ikarus_number_value_to_entity_value(IkarusNumberValue * number_value);
IKARUS_END_HEADER

View file

@ -0,0 +1,44 @@
#pragma once
/// \file text_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup entity_value Entity Values
/// @{
IKARUS_BEGIN_HEADER
/// \brief A true/false boolean-like value. For example "IsDead".
struct IkarusTextValue;
/// \brief Creates a text value from a boolean.
/// \param value The text value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextValue * ikarus_text_value_create(char const * value);
/// \brief Creates an indeterminate text value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate();
/// \brief Sets the value of a text value.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param new_value The new value.
IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value);
/// \brief Fetches the underlying value of a text value.
/// \param value The text value.
/// \return The underlying value.
/// \warning If the value is indeterminate, false is returned.
IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value);
/// \brief Converts a text value to an entity value.
/// \param text_value The text value to convert.
/// \return The converted entity value.
IKA_API struct IkarusEntityValue * ikarus_text_value_to_entity_value(IkarusTextValue * text_value);
IKARUS_END_HEADER

View file

@ -0,0 +1,44 @@
#pragma once
/// \file toggle_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
/// \addtogroup entity_value Entity Values
/// @{
IKARUS_BEGIN_HEADER
/// \brief A true/false boolean-like value. For example "IsDead".
struct IkarusToggleValue;
/// \brief Creates a toggle value from a boolean.
/// \param value The toggle value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value);
/// \brief Creates an indeterminate toggle value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate();
/// \brief Sets the value of a toggle value.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \param new_value The new value.
IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value);
/// \brief Fetches the underlying value of a toggle value.
/// \param value The toggle value.
/// \return The underlying value.
/// \warning If the value is indeterminate, false is returned.
IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value);
/// \brief Converts a toggle value to an entity value.
/// \param toggle_value The toggle value to convert.
/// \return The converted entity value.
IKA_API struct IkarusEntityValue * ikarus_toggle_value_to_entity_value(IkarusToggleValue * toggle_value);
IKARUS_END_HEADER

View file

@ -1,16 +1,11 @@
#pragma once
// IMPLEMENTATION_DETAIL_PROPERTY_TYPES
/// \file value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/macros.h>
#include <ikarus/objects/property_type.h>
#include <ikarus/stdtypes.h>
IKARUS_BEGIN_HEADER
/// \defgroup entity_value Entity Values
/// \brief The values stored in entities.
/// \details Each entity has a value for each property it is associated with.
@ -19,112 +14,47 @@ IKARUS_BEGIN_HEADER
/// \see PropertyType PropertySettings
/// @{
/// \brief A true/false boolean-like value. For example "IsDead".
struct IkarusToggleValue;
IKARUS_BEGIN_HEADER
/// \brief An arbitrary numeric value. For example "Age".
struct IkarusNumberValue;
/// \brief The common type for all values.
struct IkarusValue;
/// \brief An arbitrary textual value. For example "First Name".
struct IkarusTextValue;
/// \brief The value of an entity associated with a property.
struct IkarusEntityValue;
/// \brief Creates a toggle value from a boolean.
/// \param value The toggle value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusToggleValue * ikarus_toggle_value_create(bool value);
/// \brief Creates an indeterminate toggle value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusToggleValue * ikarus_toggle_value_create_indeterminate();
/// \brief Sets the value of a toggle value.
/// \param value The toggle value.
/// \pre Must not be null.
/// \param new_value The new value.
IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, bool new_value);
/// \brief Creates a number value from a number.
/// \param value The number value.
/// \pre Must be finite & not NaN.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberValue * ikarus_number_value_create(long double value);
/// \brief Creates an indeterminate number value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberValue * ikarus_number_value_create_indeterminate();
/// \brief Sets the value of a number value.
/// \param value The number value.
/// \pre Must not be null.
/// \param new_value The new value.
IKA_API void ikarus_number_value_set(IkarusNumberValue * value, bool new_value);
/// \brief Creates a text value from string.
/// \param value The text value.
/// \pre Must not be null.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextValue * ikarus_text_value_create(char const * value);
/// \brief Creates an indeterminate text value.
/// \return The entity value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextValue * ikarus_text_value_create_indeterminate();
/// \brief Sets the value of a text value.
/// \param value The text value.
/// \pre Must not be null.
/// \param new_value The new value.
IKA_API void ikarus_text_value_set(IkarusTextValue * value, bool new_value);
/// \brief Checks if a toggle value is indeterminate.
/// \param value The toggle value.
/// \pre Must not be null.
/// \brief Checks if a value is indeterminate.
/// \param value The value.
/// \pre \li Must not be null.
/// \return True if the value is indeterminate, false otherwise.
IKA_API bool ikarus_toggle_value_is_indeterminate(IkarusToggleValue const * value);
/// \brief Checks if a number value is indeterminate.
/// \param value The number value.
/// \pre Must not be null.
/// \return True if the value is indeterminate, false otherwise.
IKA_API bool ikarus_number_value_is_indeterminate(IkarusNumberValue const * value);
/// \brief Checks if a text value is indeterminate.
/// \param value The text value.
/// \pre Must not be null.
/// \return True if the value is indeterminate, false otherwise.
IKA_API bool ikarus_text_value_is_indeterminate(IkarusTextValue const * value);
IKA_API bool ikarus_value_is_indeterminate(IkarusValue const * value);
/// \brief Fetches the underlying value of a toggle value.
/// \param value The toggle value.
/// \return The underlying value.
/// \warning If the value is indeterminate, false is returned.
IKA_API bool ikarus_toggle_value_get_underlying(IkarusToggleValue const * value);
/// \brief Fetches the underlying value of a number value.
/// \param value The number value.
/// \return The underlying value.
/// \warning If the value is indeterminate, 0.0 is returned.
IKA_API long double ikarus_number_value_get_underlying(IkarusNumberValue const * value);
/// \brief Fetches the underlying value of a text value.
/// \param value The text value.
/// \return A copy of the underlying value.
/// \remark The returned value is a copy and owned by the caller.
/// \warning If the value is indeterminate, an empty string is returned.
IKA_API char const * ikarus_text_value_get_underlying(IkarusTextValue const * value);
// \brief Converts an entity value to a string.
// \pre \li Must not be null.
// \param value The entity value.
// \return A string representation of the value or null if an error occurred.
// \remark The returned value is a copy and owned by the caller.
IKA_API char const * ikarus_value_to_string(IkarusValue const * value);
/// \brief Visits an entity value, calling the appropriate function for the value's type.
/// \param value The entity value to visit.
/// \param toggle The function to call if the value is a toggle value. Skipped if null.
/// \param number The function to call if the value is a number value. Skipped if null.
/// \param text The function to call if the value is a text value. Skipped if null.
/// \param toggle_visitor The function to call if the value is a toggle value. Skipped if null.
/// \param number_visitor The function to call if the value is a number value. Skipped if null.
/// \param text_visitor The function to call if the value is a text value. Skipped if null.
/// \param data The data passed to the visitor functions.
IKA_API void ikarus_value_visit(
IkarusEntityValue * value,
void (*toggle)(IkarusToggleValue *, void *),
void (*number)(IkarusNumberValue *, void *),
void (*text)(IkarusTextValue *, void *),
IkarusValue * value,
void (*toggle_visitor)(struct IkarusToggleValue *, void *),
void (*number_visitor)(struct IkarusNumberValue *, void *),
void (*text_visitor)(struct IkarusTextValue *, void *),
void * data
);
/// \see ikarus_value_visit
IKA_API void ikarus_value_visit_const(
IkarusValue const * value,
void (*toggle_visitor)(struct IkarusToggleValue const *, void *),
void (*number_visitor)(struct IkarusNumberValue const *, void *),
void (*text_visitor)(struct IkarusTextValue const *, void *),
void * data
);
IKARUS_END_HEADER
/// @}

View file

@ -5,10 +5,7 @@
#include <cppbase/strings.hpp>
#include <ikarus/objects/blueprint.h>
#include <ikarus/objects/entity.h>
#include <ikarus/objects/object_type.h>
#include <id.hpp>
#include <objects/entity.hpp>
#include <objects/property.hpp>
#include <persistence/project.hpp>

87
src/objects/property.cpp Normal file
View file

@ -0,0 +1,87 @@
#include "property.hpp"
#include <cppbase/logger.hpp>
#include <cppbase/result.hpp>
#include <cppbase/strings.hpp>
#include <ikarus/values/value.h>
#include <objects/blueprint.hpp>
#include <objects/entity.hpp>
#include <objects/property.hpp>
#include <objects/property_info.hpp>
#include <objects/property_source.hpp>
#include <persistence/project.hpp>
IkarusProperty * ikarus_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
struct IkarusPropertyInfo * property_info
) {
LOG_INFO("creating new property");
if (project == nullptr) {
LOG_ERROR("project is nullptr");
return nullptr;
}
auto ctx = project->function_context();
if (property_source == nullptr) {
ctx->set_error("property_source is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input);
return nullptr;
}
if (name == nullptr) {
ctx->set_error("name is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input);
return nullptr;
}
if (cppbase::is_empty_or_blank(name)) {
ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input);
return nullptr;
}
if (property_info == nullptr) {
ctx->set_error("property_info is nullptr", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input);
return nullptr;
}
LOG_VERBOSE(
"project={}; name={}; property_source={}; property_info={}",
project->path().c_str(),
name,
std::visit(
cppbase::overload{
[](IkarusBlueprint * blueprint) { return fmt::format("Blueprint({})", blueprint->id); },
[](IkarusEntity * entity) { return fmt::format("Entity({})", entity->id); }},
property_source->data
),
std::visit(
cppbase::overload{
[](IkarusTogglePropertyInfo * info) {
return fmt::format(
"Toggle(default_value={})",
cppbase::OwningString{ikarus_value_to_string(ikarus_toggle_value_to_entity_value(info->default_value))}
.data
);
},
[](IkarusNumberPropertyInfo * info) {
return fmt::format(
"Number(default_value={})",
cppbase::OwningString{ikarus_value_to_string(ikarus_number_value_to_entity_value(info->default_value))}
.data
);
},
[](IkarusTextPropertyInfo * info) {
return fmt::format(
"Text(default_value={})",
cppbase::OwningString{ikarus_value_to_string(ikarus_text_value_to_entity_value(info->default_value))}
.data
);
}},
property_info->data
)
);
}

View file

@ -0,0 +1,74 @@
#pragma once
#include <variant>
#include <ikarus/objects/property_info.h>
#include <values/number_value.hpp>
#include <values/text_value.hpp>
#include <values/toggle_value.hpp>
// this looks a bit cursed, but there's reason to my madness:
// Let's go over the facts:
// 1. The client uses the concrete types (e.g. Ikarus"Toggle"PropertyInfo)
// 2. The API needs to accept a common type (i.e. IkarusPropertyInfo)
// 3. Casting between a concrete subtype and a common type is only defined behaviour if we use inheritance, otherwise an
// allocation is unavoidable
// 4. On the implementation side, we need to be able to distinguish between the concrete types
//
// There's a few ways to model this (using dynamic_casts, using an enum, a union, the visitor pattern, ...) but std::variant
// gives us the most type safety without any performance overhead
/// \private
struct IkarusPropertyInfo {
public:
using IkarusPropertyInfoData =
std::variant<struct IkarusTogglePropertyInfo *, struct IkarusNumberPropertyInfo *, struct IkarusTextPropertyInfo *>;
public:
inline explicit IkarusPropertyInfo(
std::variant<struct IkarusTogglePropertyInfo *, struct IkarusNumberPropertyInfo *, struct IkarusTextPropertyInfo *> data
):
data{data} {}
public:
[[nodiscard]] inline IkarusPropertyInfoData const& get_data() const {
return data;
}
private:
IkarusPropertyInfoData data;
};
/// \private
struct IkarusTogglePropertyInfo : public IkarusPropertyInfo {
public:
inline IkarusTogglePropertyInfo():
IkarusPropertyInfo{this} {}
public:
[[nodiscard]] IkarusToggleValue * get_default_value() const {}
private:
IkarusToggleValue * default_value{nullptr};
};
/// \private
struct IkarusNumberPropertyInfo : public IkarusPropertyInfo {
public:
inline IkarusNumberPropertyInfo():
IkarusPropertyInfo{this} {}
private:
IkarusNumberValue * default_value{nullptr};
};
/// \private
struct IkarusTextPropertyInfo : public IkarusPropertyInfo {
public:
inline IkarusTextPropertyInfo():
IkarusPropertyInfo{this} {}
private:
IkarusTextValue * default_value{nullptr};
};

View file

@ -0,0 +1,10 @@
#pragma once
#include <variant>
#include <ikarus/objects/property_source.h>
/// \private
struct IkarusPropertySource {
std::variant<IkarusBlueprint *, IkarusEntity *> data;
};

View file

@ -13,6 +13,7 @@
constexpr inline size_t MAXIMUM_ERROR_INFOS = 8;
constexpr inline size_t MAXIMUM_ERROR_MESSAGE_LENGTH = 256;
/// \private
class FunctionContext {
public:
explicit FunctionContext(struct IkarusProject * project);
@ -128,7 +129,7 @@ FunctionContext::~FunctionContext() {
template<typename... Infos>
requires(std::is_same_v<IkarusErrorInfo, Infos> && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS)
auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) {
auto FunctionContext::set_error(std::string_view error_message, bool log_error, Infos... infos) -> void {
if (error_message.size() > _project->error_message_buffer.size()) {
_project->error_message_buffer.resize(error_message.size() + 1);
}

View file

@ -0,0 +1,5 @@
#pragma once
struct IkarusNumberValue {
long double value;
};

View file

@ -0,0 +1,8 @@
#pragma once
#include <string>
/// \private
struct IkarusTextValue {
std::string value;
};

View file

@ -0,0 +1,6 @@
#pragma once
/// \private
struct IkarusToggleValue {
bool value;
};