[beast: 3/6] BSE: store and load C++ object properties
- From: Tim Janik <timj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [beast: 3/6] BSE: store and load C++ object properties
- Date: Mon, 7 Sep 2015 21:42:45 +0000 (UTC)
commit 7ec85b8a81c066d9090b65eff0b6f41bf0159634
Author: Tim Janik <timj gnu org>
Date: Sun Aug 16 01:54:07 2015 +0200
BSE: store and load C++ object properties
bse/bsestorage.cc | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 186 insertions(+), 6 deletions(-)
---
diff --git a/bse/bsestorage.cc b/bse/bsestorage.cc
index d6f759c..943e79f 100644
--- a/bse/bsestorage.cc
+++ b/bse/bsestorage.cc
@@ -602,14 +602,148 @@ item_link_resolved (gpointer data,
}
}
+/// Retrive the value of @a field.key through aux_vector_find() and convert the value to @a ValueType.
+template<typename ValueType = String> ValueType
+aux_vector_get (const std::vector<String> &auxvector, const String &field, const String &key, const String
&fallback = "")
+{
+ return Rapicorn::string_to_type<ValueType> (Rapicorn::Aida::aux_vector_find (auxvector, field, key,
fallback));
+}
+
+static bool
+any_set_from_string (Any &any, const String &string)
+{
+ using namespace Rapicorn;
+ switch (any.kind())
+ {
+ case Aida::BOOL: any.set (string_to_bool (string)); break;
+ case Aida::INT64: any.set (string_to_int (string)); break;
+ case Aida::FLOAT64: any.set (string_to_double (string)); break;
+ case Aida::STRING: any.set (string_from_cquote (string)); break;
+ default: assert (!"reached");
+ }
+ return true;
+}
+
+static GTokenType
+scanner_parse_paren_rest (GScanner *scanner, String *result)
+{
+ // configure scanner to pass through most characters, so we can delay parsing
+ const GScannerConfig saved_config = *scanner->config;
+ scanner->config->cset_identifier_first = (char*) "";
+ scanner->config->cset_identifier_nth = (char*) "";
+ scanner->config->cset_skip_characters = (char*) " \t\r\n";
+ scanner->config->scan_identifier = false;
+ scanner->config->scan_identifier_1char = false;
+ scanner->config->scan_symbols = false;
+ scanner->config->scan_binary = false;
+ scanner->config->scan_octal = false;
+ scanner->config->scan_float = false;
+ scanner->config->scan_hex = false;
+ scanner->config->scan_hex_dollar = false;
+ scanner->config->char_2_token = false;
+ GTokenType expected_token = G_TOKEN_NONE, token = g_scanner_get_next_token (scanner);
+ uint level = 1; // need one ')' to terminate
+ String rest;
+ while (token && expected_token == G_TOKEN_NONE)
+ {
+ if (token == G_TOKEN_CHAR)
+ {
+ if (scanner->value.v_char == '(')
+ level += 1;
+ else if (scanner->value.v_char == ')')
+ {
+ level -= 1;
+ if (level == 0)
+ break;
+ }
+ else
+ rest += scanner->value.v_char;
+ }
+ else if (token == G_TOKEN_STRING)
+ {
+ if (!rest.empty())
+ rest += " ";
+ rest += Rapicorn::string_to_cquote (scanner->value.v_string);
+ }
+ else if (token == G_TOKEN_INT)
+ {
+ if (!rest.empty())
+ rest += " ";
+ rest += Rapicorn::string_from_int (scanner->value.v_int);
+ }
+ else
+ {
+ expected_token = G_TOKEN_RIGHT_PAREN; // was expecting a char, got something unknown
+ break;
+ }
+ token = g_scanner_get_next_token (scanner);
+ }
+ *scanner->config = saved_config;
+ if (result && expected_token == G_TOKEN_NONE)
+ *result = rest;
+ return expected_token; // G_TOKEN_NONE on success
+}
+
+static GTokenType
+storage_parse_property_value (BseStorage *self, const String &name, Any &any, const std::vector<std::string>
&aux_data)
+{
+ using namespace Rapicorn;
+ assert_return (BSE_IS_STORAGE (self), G_TOKEN_ERROR);
+ GScanner *scanner = bse_storage_get_scanner (self);
+ String rest;
+ GTokenType expected_token = scanner_parse_paren_rest (scanner, &rest);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ const bool any_from_string_conversion = any_set_from_string (any, rest);
+ if (any_from_string_conversion)
+ return G_TOKEN_NONE;
+ else
+ {
+ bse_storage_error (self, "failed to parse Any from: \"%s\"", rest.c_str());
+ return G_TOKEN_ERROR;
+ }
+}
+
+static GTokenType
+restore_cxx_item_property (BseItem *bitem, BseStorage *self)
+{
+ using namespace Rapicorn;
+ GScanner *scanner = bse_storage_get_scanner (self);
+ Bse::ItemImpl *item = bitem->as<Bse::ItemImpl*>();
+ // need identifier
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
+ return SFI_TOKEN_UNMATCHED;
+ const String identifier = scanner->next_value.v_identifier;
+ // find identifier in item, we could search __aida_dir__, but *getting* is simpler
+ Any any = item->__aida_get__ (identifier);
+ if (any.kind())
+ {
+ const std::vector<String> auxvector = item->__aida_aux_data__();
+ // FIXME: need special casing of object references, see bse_storage_parse_item_link
+ parse_or_return (scanner, G_TOKEN_IDENTIFIER); // eat pspec name
+ // parse Any value, including the closing ')'
+ GTokenType expected_token = storage_parse_property_value (self, identifier, any, auxvector);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ if (Rapicorn::Aida::aux_vector_check_options (auxvector, identifier, "hints", "r:w:S")) // readable,
writable, storage
+ {
+ item->__aida_set__ (identifier, any);
+ }
+ else
+ bse_storage_warn (self, "ignoring non-writable object property \"%s\" of type '%s'",
+ identifier, Aida::type_kind_name (any.kind()));
+ return G_TOKEN_NONE;
+ }
+ return SFI_TOKEN_UNMATCHED;
+}
+
static GTokenType item_restore_try_statement (gpointer item, BseStorage *self, GScanner *scanner, gpointer
user_data);
+
static GTokenType
-restore_item_property (BseItem *item,
- BseStorage *self)
+restore_item_property (BseItem *item, BseStorage *self)
{
GScanner *scanner = bse_storage_get_scanner (self);
GTokenType expected_token;
- GParamSpec *pspec;
GValue value = { 0, };
/* check identifier */
if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
@@ -621,9 +755,9 @@ restore_item_property (BseItem *item,
* them to SFI_PARAM_SERVE_STORAGE and SFI_PARAM_SERVE_GUI
* at some point...)
*/
- pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (item), scanner->next_value.v_identifier);
+ GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (item),
scanner->next_value.v_identifier);
if (!pspec)
- return SFI_TOKEN_UNMATCHED;
+ return restore_cxx_item_property (item, self);
parse_or_return (scanner, G_TOKEN_IDENTIFIER); /* eat pspec name */
/* parse value, special casing object references */
if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), BSE_TYPE_ITEM))
@@ -655,7 +789,7 @@ restore_item_property (BseItem *item,
g_object_set_property (G_OBJECT (item), /* no undo */
pspec->name, &value);
else
- bse_storage_warn (self, "ignoring non-writable object property \"%s\" of type `%s'",
+ bse_storage_warn (self, "ignoring non-writable object property \"%s\" of type '%s'",
pspec->name, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
g_value_unset (&value);
return G_TOKEN_NONE;
@@ -1078,6 +1212,51 @@ store_item_properties (BseItem *item,
g_free (pspecs);
}
+static void
+storage_store_property_value (BseStorage *self, const String &property_name, Any any, const
std::vector<std::string> &aux_data)
+{
+ using namespace Rapicorn;
+ if (Rapicorn::Aida::aux_vector_check_options (aux_data, property_name, "hints", "skip-default"))
+ {
+ Any dflt;
+ const char *const invalid = "\377\377\376\376\1\2 invalid \3"; // no-value marker, (invalid UTF-8)
+ const String dflt_val = aux_vector_get (aux_data, property_name, "default", invalid);
+ if (dflt_val != invalid)
+ {
+ dflt = any; // copy type
+ const bool any_from_string_conversion = any_set_from_string (any, dflt_val);
+ if (any_from_string_conversion && dflt == any)
+ return; // skip storing default value
+ }
+ }
+ String target;
+ switch (any.kind())
+ {
+ case Aida::BOOL: target = string_from_bool (any.get<bool>()); break;
+ case Aida::INT64: target = string_from_int (any.get<int64>()); break;
+ case Aida::FLOAT64: target = string_from_double (any.get<double>()); break;
+ case Aida::STRING: target = string_to_cquote (any.get<String>()); break;
+ default: assert (!"reached");
+ }
+ assert (!target.empty());
+ bse_storage_break (self);
+ bse_storage_putc (self, '(');
+ bse_storage_puts (self, property_name.c_str());
+ bse_storage_putc (self, ' ');
+ bse_storage_puts (self, target.c_str());
+ bse_storage_putc (self, ')');
+}
+
+static void
+store_cxx_item_properties (BseItem *bitem, BseStorage *self)
+{
+ Bse::ItemImpl *item = bitem->as<Bse::ItemImpl*>();
+ const std::vector<String> auxvector = item->__aida_aux_data__();
+ for (const String &pname : item->__aida_dir__())
+ if (Rapicorn::Aida::aux_vector_check_options (auxvector, pname, "hints", "r:w:S")) // readable,
writable, storage
+ storage_store_property_value (self, pname, item->__aida_get__ (pname), auxvector);
+}
+
void
bse_storage_store_item (BseStorage *self, BseItem *item)
{
@@ -1087,6 +1266,7 @@ bse_storage_store_item (BseStorage *self, BseItem *item)
g_object_ref (self);
g_object_ref (item);
sfi_ppool_set (self->stored_items, item);
+ store_cxx_item_properties (item, self);
store_item_properties (item, self);
BSE_OBJECT_GET_CLASS (item)->store_private (BSE_OBJECT (item), self);
bse_parasite_store (BSE_OBJECT (item), self);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]