Generated reference › API — ICoreBlocks/ICoreBlockLibrary
kind: generated#api#icoreblocks-icoreblocklibrary

API — ICoreBlocks/ICoreBlockLibrary

The public contract of 5 header(s) under src/ICoreBlocks/ICoreBlockLibrary — 7 class/struct definition(s), 32 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.

ICoreCRuntime.h#

src/ICoreBlocks/ICoreBlockLibrary/Runtimes/CRuntime/ICoreCRuntime.h

ICoreCBlockProgram#

ICoreCRuntime.h:24 · class · pImpl · 3 declaration(s)

One compiled user program: the user's C source, prefixed with the runtime's prelude (the ICoreCMatrix struct + helpers), compiled by the system C compiler into a shared library in a private temp di...

class ICoreCBlockProgram {
public:
    ICoreCBlockProgram();
    ~ICoreCBlockProgram();

    // Reload the shared library so the user code's statics are re-zeroed
    // (called at every simulation start). False + errorOut when the reload
    // fails; the program is unusable afterwards and must be recompiled.
    bool resetState(std::string& errorOut);

    // One solver step: compute(t, u, uCount, y, yCount). Every u entry is
    // handed to the user as a row-major ICoreCMatrix; y arrives as
    // `outputCount` matrices whose rows/cols the USER must set (they start
    // at 0x0), each with a data capacity of ICORE_MAX_MATRIX_ELEMENTS
    // doubles. Unlike Python — where the returned list's length carries the
    // arity — C outputs are caller-allocated, so the expected count is a
    // parameter. Returns false with a compiler/loader/contract error message
    // without touching yOut.
    //
    // Every port is ICoreDouble, which is what this runtime assumed before
    // signal types existed. Forwards to the typed form below with empty type
    // lists, so a double-only block's crossing is unchanged byte for byte.
    bool run(double t, const std::vector<ICoreMatrix>& u, size_t outputCount,
             std::vector<ICoreMatrix>& yOut, std::string& errorOut);

    // The typed form. `uTypeIds` / `yTypeIds` are signal-type registry ids
    // ("ICoreDouble", "ICoreInt32", "ICoreString", …), one per port; an EMPTY
    // list means all-double, and so does an id the registry does not know.
    // They reach the user's code as each matrix's read-only `kind` tag, which
    // is why they are worth carrying at all: a C block cannot ask a port
    // anything, so without the tag it cannot tell an int32 input from a double
    // one and must guess.
    //
    // `uText` carries a String input's value (indexed like `u`; entries for
    // numeric ports are ignored) and `yTextOut` receives a String output's.
    // They are separate from the matrices because a string cannot ride in one:
    // a String port's matrix is a 1x1 zero placeholder and the value lives
    // beside it, which is exactly the shape the port's own
    // solver environment already stores it in.
    //
    // yTextOut is sized to outputCount on success; a numeric output's entry is
    // empty. A String output does NOT have to set rows/cols — its matrix is a
    // placeholder, so 1x1 is pre-set for it and the "compute left rows/cols at
    // 0x0" error cannot fire on one.
    bool run(double t,
             const std::vector<ICoreMatrix>& u,
             const std::vector<std::string>& uTypeIds,
             const std::vector<std::string>& uText,
             size_t outputCount,
             const std::vector<std::string>& yTypeIds,
             std::vector<ICoreMatrix>& yOut,
             std::vector<std::string>& yTextOut,
             std::string& errorOut);

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICoreCRuntime#

ICoreCRuntime.h:85 · class · 2 declaration(s)

class ICoreCRuntime {
public:
    // False when no C compiler (cc / clang / gcc) is on the PATH.
    static bool isAvailable();

    // First line of `<compiler> --version` plus its path — for diagnostics.
    static std::string runtimeInfo();

    // Element capacity of every output matrix handed to compute — the
    // prelude's ICORE_MAX_MATRIX_ELEMENTS.
    static constexpr size_t MAX_MATRIX_ELEMENTS = 4096;

    // Writes prelude + user source to a temp dir, compiles it into a shared
    // library and validates it exports compute (the prelude's prototype makes
    // a wrong signature a compile error, not a mid-run surprise). Null +
    // errorOut (the compiler's stderr included) on any failure. Thread-safe;
    // may be called from the solver worker thread.
    static std::unique_ptr<ICoreCBlockProgram> compileBlockProgram(
        const std::string& sourceCode, std::string& errorOut);
};
};

ICorePythonRuntime.h#

src/ICoreBlocks/ICoreBlockLibrary/Runtimes/PythonRuntime/ICorePythonRuntime.h

ICorePythonBlockProgram#

ICorePythonRuntime.h:10 · class · pImpl · 3 declaration(s)

One compiled user program (a module scope that must define compute(t, u, state)).

class ICorePythonBlockProgram {
public:
    ICorePythonBlockProgram();
    ~ICorePythonBlockProgram();

    // Fresh empty `state` dict (called at every simulation start).
    void resetState();

    // One solver step: y = compute(t, u, state). Every u entry is handed to
    // the user as a 2D numpy float matrix; the returned sequence is coerced
    // back to 2D ICoreMatrix values (scalars/1D rows are promoted via
    // numpy.atleast_2d). Returns false with a Python-style error message —
    // wrong return arity/shape included — without touching yOut.
    //
    // Every port is ICoreDouble, which is what this runtime assumed before
    // signal types existed. Forwards to the typed form below with empty type
    // lists, so a double-only block's crossing is unchanged.
    //
    // ⚠ NOTHING IN THIS TREE CALLS IT ANY MORE, deliberately: `Python_Code`'s
    // SIZING call used to, and sizing a block through a boundary that is not
    // the one it will run through is how a Bool port arrived as an array at
    // sizing and as a `bool` at t = 0. It is kept because
    // ICoreCBlockProgram carries the same pair and an untyped caller is a
    // reasonable thing for an SDK to offer — but if you are reaching for it
    // from inside this tree, you almost certainly want the typed form.
    bool run(double t, const std::vector<ICoreMatrix>& u,
             std::vector<ICoreMatrix>& yOut, std::string& errorOut);

    // The typed form, and the counterpart of ICoreCBlockProgram's. `uTypeIds`
    // / `yTypeIds` are signal-type registry ids, one per port; an EMPTY list
    // means all-double, and so does an id the registry does not know.
    //
    // WHAT A TYPE LOOKS LIKE FROM PYTHON. C gets a `kind` tag because a C
    // struct has nowhere else to put one; numpy already has the answer, so an
    // input port arrives as an array of ITS OWN DTYPE — bool_ for a Boolean,
    // int32 for an ICoreInt32, float32 for an ICoreSingle. `u[i].dtype` is
    // therefore how a Python block asks what a port is, and it needs no
    // vocabulary of ours to do it.
    //
    // …EXCEPT AT 1x1, where a Boolean or Integer port hands over a native
    // Python `bool` / `int` instead, so that `if u[0]:` and `u[0] % 2` mean
    // what a Python author expects rather than what a 1x1 array does. A
    // FLOATING port keeps its array at every size, ICoreDouble included: every
    // block written against this runtime indexes u[i] as a matrix, and an
    // ICoreSingle needs the array because a Python float is a double and the
    // dtype is the only place float32 survives.
    //
    // THE RETURNED LIST IS ADMITTED PER OUTPUT PORT, NOT QUANTIZED. A value
    // the port's type cannot carry — text for a numeric port, an array for a
    // String one — is refused with a message naming the output's position in
    // the caller's own return list, the type the port was declared with, and
    // what came back. Applying the KIND is not this boundary's job and must not
    // become it: that happens exactly once, at the port write
    // (ICorePortSolverEnvironment::quantizeSignalToType).
    //
    // `uText` carries a String input's value and `yTextOut` receives a String
    // output's, for the same reason as in the C runtime: a string cannot ride
    // in the matrix, so it travels beside it (a String port's matrix is a 1x1
    // zero placeholder). A String output may be returned as a `str` or as
    // anything `str()` accepts; yTextOut is sized to the returned arity on
    // success and a numeric output's entry is empty.
    bool run(double t,
             const std::vector<ICoreMatrix>& u,
             const std::vector<std::string>& uTypeIds,
             const std::vector<std::string>& uText,
             const std::vector<std::string>& yTypeIds,
             std::vector<ICoreMatrix>& yOut,
             std::vector<std::string>& yTextOut,
             std::string& errorOut);

private:
    class Impl;                    // the two-line residue; state lives here
    std::unique_ptr<Impl> impl;
};

ICorePythonRuntime#

ICorePythonRuntime.h:87 · class · 2 declaration(s)

class ICorePythonRuntime {
public:
    // False when the app was built without ICORE_PYTHON_EMBED (no headers /
    // libpython at build time) or the interpreter failed to boot.
    static bool isAvailable();

    // "Python 3.14.4 | numpy 2.3.5" — for diagnostics.
    static std::string runtimeInfo();

    // Compiles user source in a fresh module scope (numpy pre-imported as
    // both `np` and `numpy`) and validates it defines a callable
    // compute(t, u, state) taking exactly 3 parameters. Null + errorOut on
    // any failure. Thread-safe; may be called from the solver worker thread.
    static std::unique_ptr<ICorePythonBlockProgram> compileBlockProgram(
        const std::string& sourceCode, std::string& errorOut);
};
};

ICoreUserBlockDefinition.h#

src/ICoreBlocks/ICoreBlockLibrary/UserBlocks/ICoreUserBlockDefinition.h

ICoreUserBlockDefinition#

ICoreUserBlockDefinition.h:33 · class · 9 declaration(s)

One parsed .iblock file — a USER-DEFINED block definition, the unit the Block Wizard writes and the user block library (ICoreUserBlockLibrary) scans.

class ICoreUserBlockDefinition {
public:
    enum class Kind {
        Composite,   // body = recipe text (ICoreRecipeSerializer output)
        Python       // body = Python source with a compute(t, u, state)
    };

    // ---- identity ---------------------------------------------------------
    ICoreString uuid;       // v4; identity across renames — never regenerate on edit
    ICoreString name;       // leaf segment, identifier-safe [A-Za-z0-9_]
    ICoreString family;     // middle segment, identifier-safe
    ICoreString summary;    // one line, shown by the navigator entry
    ICoreString created;    // "YYYY-MM-DD"; informational, not validated
    ICoreString modified;
    Kind kind = Kind::Composite;

    // ---- ports ------------------------------------------------------------
    // Counts are what the navigator preview draws (the analogue of
    // registerInitialPorts). Labels are empty or exactly count-sized; a side
    // with 2+ ports MUST carry labels — same rule as ADDING_NEW_BLOCKS.md §3,
    // and validate() enforces it. Labels may not contain commas (they are
    // comma-separated in the header).
    int inputCount = 0;
    int outputCount = 0;
    ICoreStringList inputLabels;
    ICoreStringList outputLabels;

    // Signal-type ids, one per port, or EMPTY meaning "all ICoreDouble".
    //
    // Empty is what every .iblock written before this key existed carries, and
    // it is not a special case to remember: it is the same thing the counts
    // already do for labels, and it is why an old file loads unchanged. A
    // present list must be exactly count-sized and every entry must be a
    // registry id -- validate() enforces both, so a definition that parsed is
    // one the model will accept.
    ICoreStringList inputTypes;
    ICoreStringList outputTypes;

    // The type of one port, resolved: the declared id, or ICoreDouble when the
    // list is absent or short. The three callers that build ports from a
    // definition ask through this rather than indexing, so "absent means
    // double" is stated once.
    [[nodiscard]] ICoreString inputTypeAt(int index) const;
    [[nodiscard]] ICoreString outputTypeAt(int index) const;

    // ---- payloads ---------------------------------------------------------
    ICoreString iconSvg;          // may be empty: navigator falls back to a monogram
    ICoreString descriptionHtml;  // may be empty: hover card shows the summary
    ICoreString body;             // must be non-empty for Kind::Python

    // ---- file text <-> definition -----------------------------------------
    // False leaves `err` explaining which line or section refused, in words a
    // user can act on ("line 4: ...", "missing section ...ICON...").
    // `out` is reset first and is only trustworthy when parse returns true.
    static bool parse(const ICoreString& fileText, ICoreUserBlockDefinition& out,
                      ICoreString& err);

    // The exact text parse() accepts. Assumes validate() passed — serialize
    // does not re-check.
    ICoreString serialize() const;

    // The rules a definition must satisfy before it is written or listed:
    // uuid present, identifier-safe name/family, non-negative counts, label
    // count/mandatory-label rules, non-empty Python body. parse() runs this
    // last, so a parsed definition is always a valid one.
    bool validate(ICoreString& err) const;

    // ---- naming -----------------------------------------------------------
    // "My_Blocks/<family>/<name>" — the navigator address. A pseudo-type: it
    // is never registered anywhere, and the grand family is reserved for us
    // (USER_BLOCK_WIZARD.md §8).
    ICoreString pseudoType() const;
    static const char* grandFamily();

    // ---- helpers ----------------------------------------------------------
    static bool isIdentifierSafe(const ICoreString& s);   // non-empty, [A-Za-z0-9_]
    static ICoreString generateUuid();                    // random v4, lowercase
    static ICoreString kindName(Kind kind);               // "composite" / "python"
    static bool kindFromName(const ICoreString& text, Kind& out);

    static constexpr int formatMajorVersion = 1;

    bool operator==(const ICoreUserBlockDefinition& other) const;
    bool operator!=(const ICoreUserBlockDefinition& other) const;
};
};

ICoreUserBlockLibrary.h#

src/ICoreBlocks/ICoreBlockLibrary/UserBlocks/ICoreUserBlockLibrary.h

ICoreUserBlockLibrary#

ICoreUserBlockLibrary.h:37 · class · nested SkippedFile · 13 declaration(s)

The user's per-app library of .iblock definitions: one folder tree under the application home, scanned off disk, never cached across calls.

class ICoreUserBlockLibrary {
public:
    // One unreadable file from the last scan, and why. The navigator's manage
    // dialog lists these — a silently absent block reads as "the app lost my
    // work", which is worse than any error message.
    struct SkippedFile {
        ICoreString path;
        ICoreString reason;
    };

    // Every definition on disk right now, family folders in name order, files
    // in name order inside each. Rescans on every call (the set is small, and
    // a cache would go stale the moment a user drops a file in with the app
    // running — the same reasoning as ICoreTemplateLibrary::all()).
    static ICoreList<ICoreUserBlockDefinition> all();

    // What the most recent scan could not read. Refreshed by all() and by the
    // mutating calls below (each rescans to validate its inputs).
    static ICoreList<SkippedFile> skippedInLastScan();

    // Lookups over a fresh scan. False leaves `out` untouched.
    static bool findByUuid(const ICoreString& uuid, ICoreUserBlockDefinition& out);
    static bool findByPseudoType(const ICoreString& pseudoType, ICoreUserBlockDefinition& out);

    // Family folder names (sorted, may include empty families) — the wizard's
    // family combo lists these.
    static ICoreStringList families();

    // Write `def` into the library at pathFor(def). Collisions:
    //   * target file exists with the SAME uuid    -> update in place;
    //   * target file exists with a DIFFERENT uuid -> refused unless
    //     `overwrite` (the wizard asks the user first);
    //   * same uuid already lives at a DIFFERENT path -> treated as a
    //     rename/move: the old file is removed after the new one is written.
    // False leaves `err` filled and the library unchanged.
    static bool save(const ICoreUserBlockDefinition& def, bool overwrite, ICoreString& err);

    // Bring an external .iblock into the library: parse + validate `source`,
    // then save() it where its own header says it belongs. `imported` receives
    // the definition on success (the caller wants the name for its "Imported
    // <block>" message). The source file is never modified.
    static bool importFile(const std::filesystem::path& source, bool overwrite,
                           ICoreUserBlockDefinition& imported, ICoreString& err);

    // Copy the block's library file to `destination`, byte for byte. Refuses
    // to overwrite an existing destination — the caller's file dialog owns
    // that conversation.
    static bool exportTo(const ICoreString& uuid, const std::filesystem::path& destination,
                         ICoreString& err);

    // Delete the block's file; prunes its family folder if that leaves it
    // empty. False when no such uuid or the delete failed.
    static bool remove(const ICoreString& uuid, ICoreString& err);

    // "<application home>/Blocks". Created on first look, like the user
    // templates folder, so there is always somewhere to drop a file.
    static std::filesystem::path userBlocksFolderPath();

    // TEST SEAM. The regression sandbox repoints the DOCUMENTS folder but the
    // application home is deliberately assigned once and never moves — so a
    // sandboxed case points this library at a scratch tree instead. An empty
    // path restores the real app home. Never called outside the test suite.
    static void setRootOverrideForTests(const std::filesystem::path& root);

    // "<root>/<family>/<name>.iblock" for this definition.
    static std::filesystem::path pathFor(const ICoreUserBlockDefinition& def);

    // Change notification, toolkit-free: save/import/remove fire every
    // subscriber after the library has changed on disk. The navigator's
    // rebuild hangs off this. Subscribers run on the caller's thread.
    static int subscribeToChanges(std::function<void()> onChanged);
    static void unsubscribeFromChanges(int token);

    // The drag-and-drop format a user-block navigator entry carries and the
    // canvas accepts; the payload is a definition's uuid. Declared here next
    // to the uuids it quotes, for the same no-drift reason as
    // ICoreTemplateLibrary::dragMimeType().
    static const char* dragMimeType();
};
};

ICoreUserBlockMaterializer.h#

src/ICoreBlocks/ICoreBlockLibrary/UserBlocks/ICoreUserBlockMaterializer.h

ICoreUserBlockMaterializer#

ICoreUserBlockMaterializer.h:33 · class · 0 declaration(s)

Stamps a user block definition into a live diagram (USER_BLOCK_WIZARD.md §7).

class ICoreUserBlockMaterializer {
public:
    // Stamp `def` into `parent` at `position` (origin-anchor coordinates,
    // +y up, exactly like move()). `createdName` receives the name the
    // instance actually got — a sibling clash gets a numbered name instead.
    static ICoreRecipeFileTransfer::Result stamp(ICoreSubsystemTreeNode* parent,
                                                 const ICoreUserBlockDefinition& def,
                                                 const ICorePoint& position,
                                                 ICoreString* createdName = nullptr);

    // The drop handler's form: resolve `uuid` in ICoreUserBlockLibrary, then
    // stamp. Fails with a user-readable reason when the uuid is not in the
    // library (e.g. the file was deleted while the navigator still showed it).
    static ICoreRecipeFileTransfer::Result stampByUuid(ICoreSubsystemTreeNode* parent,
                                                       const ICoreString& uuid,
                                                       const ICorePoint& position,
                                                       ICoreString* createdName = nullptr);
};
};