Design/StorageImplementationNotes
From Dyna
A term declaration must contain:
name = "foo", // name of the term on c++ side
storage = direct, // or indirect_nointern, or indirect_intern
It may contain:
args = [ "atype", "anothertype" ], % list of C++ side dyna terms
sval_type = "afurthertype", % stored value type of the term
sval_default = "afurthertype( afurthertypearg )" % constructor of sval_type to use; default: no_default
primitive = "string" % this is akin to args = [ "string" ] and sval_type = name,
% with special glue code for correct behavior of primitive types
% (namely, sval() returns *this). Must be disjoint from args/sval declarations
Note on "sval_default = no_default" :
This option neccesiates, in some cases, the keeping of a valid bit. It must be kept when:
1) The term is interned
2) The term is *not* interned, but is symbiotic with another term.
It may not be kept when:
1) sval is the only field in it's struct
In such a case we simply check for its presence.
// Example 1:
paramaters_to_storage_layer = [
sterm(
name = "edge",
args = ["vertexID", "vertexID"],
sval_type = "int_t",
sval_default = "int_t(INT_MAX)",
storage = direct,
),
]
// Output of the storage layer:
struct struct1 {
vertexID field1;
vertexID field2;
hash_val hash() const; // some combination of field1.hash() & field2.hash()
// field1 == rhs.field1 && field2 == rhs.field2
bool operator == ( const struct1 & ) const;
// field1 < rhs.field1 || field1 == rhs.field1 && field2 < rhs.field2
bool operator < ( const struct1 & ) const;
}
struct struct2 {
int_t field1;
}
hash_map< struct1, struct2 > edge_hash; // < key, value >
class edge {
// user exposed constructor
edge( const vertexID & , const vertexID & ); // assign to struct1
struct1 mem1;
inline const vertexID & arg0() const;
inline const vertexID & arg1() const;
hash_val hash() const; // mem1.hash();
const int_t & get_sval() const; // edge_hash.lookup( mem1 ).field1;
const int_t & set_sval( const int_t & );
bool operator == ( const edge & ) const; // arg0() == rhs.arg0() && arg1() == rhs.arg1()
bool operator < ( const edge & ) const; // arg0() < rhs.arg0() || arg0() == rhs.arg0() && arg1() < rhs.arg1()
}
/* Point1: arg0, arg1, and sval are additionally implicit access methods,
* upon which others can be built. Further access methods should
* have no need to visit struct1 and struct2 directly, correct?
* They can rely upon the edge interface, or a pointer reinterpretation */
/* Point2: I'm not sure there's any distinction between a shallow and deep
* equality method from a structure's point of view, as a struct never contains
* a term pointer, just a term itself. It's up to the term whether it does a
* pointer check (interned case) or a recursive (deep) equality test. */
/* Point3: Structs, terms, and primitives should also expose less-than
* operators. This allows for sorted containers and space efficient intern
* strategies like treemap/treeset */
// Example 2:
paramaters_to_storage_layer = [
sterm(
name = "foo",
args = ["vertexID", "int_t", "string_t", ],
sval_type = "int_t",
sval_default = "int_t(INT_MAX)",
storage = indirect_nointern,
),
]
// Output of the storage layer:
struct struct3 {
vertexID field1;
int_t field2;
string_t field3;
hash_val hash() const; // combine field(1-3).hash()
bool operator == ( const struct3 & ) const;
bool operator < ( const struct3 & ) const; // field1 < rhs.field1 || field1 == rhs.field1 && (field2 < rhs.field2 || field2 == rhs.field2 && field3 < rhs.field3
}
struct struct4 {
int_t field1;
}
hash_map< struct3, struct4 > foo_hash; // < key, value >
class foo {
foo( vertexID, int_t, string_t ); // maybe allocate a struct3 and assign
struct3 * mem1;
inline const vertexID & arg0() const;
inline const dyna_int & arg1() const;
inline const dyna_string & arg2() const;
hash_val hash() const; // mem1->hash();
const int_t & get_sval() const; // foo_hash.lookup( *mem1 ).field1;
const int_t & set_sval( const int_t & );
bool operator == ( const foo & ) const; // arg0() == rhs.arg0 && arg1() == rhs.arg1() && arg2 == rhs.arg2()
bool operator < ( const foo & ) const; // arg0() < rhs.arg0() || arg0() == rhs.arg0() && (arg1() < rhs.arg1() || arg1() == rhs.arg1() && arg2() < rhs.arg2()
}
// Example 3:
paramaters_to_storage_layer = [
sterm(
name = "bar",
args = ["baz", "int_t", ],
sval_type = "edge",
sval_default = no_default,
storage = indirect_intern,
),
]
// Output of the storage layer:
struct struct5 {
baz field1;
int_t field2;
edge field3;
char bits; // extra bit collection
hash_val hash() const; // combine field1.hash() & field2.hash() ONLY
bool operator == ( const struct5 & ) const; // field1 == rhs.field1 && field2 == rhs.field2 ONLY
bool operator < ( const struct5 & ) const; // field1 < rhs.field1 || field1 == rhs.field1 && field2 < rhs.field2 ONLY
}
hash_set< struct5 > bar_hash; // < key_value >
class bar {
// user exposed constructor
bar( baz, dyna_int ); // allocate & assign struct 5 to var1; mem1 = bar_hash.find_or_insert( *var1 );
struct5 * mem1;
inline const baz & arg0() const;
inline const dyna_int & arg1() const;
hash_val hash() const; // mem1->hash();
bool has_sval(); // mask test of mem1->bits
const edge & get_sval() const; // checked mem1->field3
const edge & set_sval( const edge & );
bool operator == ( const edge & ) const; // arg0() == rhs.arg0() && arg1() == rhs.arg1()
bool operator < ( const bar & ) const; // arg0() < rhs.arg0() || arg0() == rhs.arg0() && arg1() < rhs.arg1()
}
// Example 4:
paramaters_to_storage_layer = [
sterm(
name = "string_t",
primitive = "string",
storage = indirect_intern,
),
]
// Output of the storage layer:
struct struct6 {
string field1; // string is c++ wrapper around char*
int hash() const; // field1.hash()
bool operator == ( const struct6 & ) const; // field1 == rhs.field1
bool operator < ( const struct6 & ) const; // field1 < rhs.field1
}
hash_set< struct6 > string_t_hash; // < key_value >
class string_t {
// user exposed constructor
string_t( string ); // allocate & assign struct 6 to var1; mem1 = string_t_hash.find_or_insert( *var1 );
struct6 * mem1;
inline const string & arg0() const; // mem1->field1;
int hash() const; // mem1->hash();
string_t & sval(); // *this <- since this is a primitive type
bool operator == ( const string_t & ) const; // arg0() == rhs.arg0()
bool operator < ( const string_t & ) const; // arg0() < rhs.arg0()
}
