Try and fail to make nodes object-safe in node graph

This commit is contained in:
Dennis 2022-04-02 20:50:53 +02:00 committed by Keavon Chambers
parent 800fb4dbc1
commit 3243b80cf2
12 changed files with 200 additions and 64 deletions

9
node-graph/Cargo.lock generated
View File

@ -93,6 +93,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "const-default"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
[[package]]
name = "countme"
version = "3.0.1"
@ -235,6 +241,9 @@ dependencies = [
[[package]]
name = "graphene-core"
version = "0.1.0"
dependencies = [
"const-default",
]
[[package]]
name = "graphene-std"

View File

@ -6,5 +6,9 @@ description = "Api definitions for graphene"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
const_default = ["const-default"]
default = ["const_default"]
[dependencies]
const-default = { version = "1.0", optional = true }

View File

@ -1,6 +1,6 @@
use std::{borrow::Borrow, marker::PhantomData};
use graphene_core::Node;
use crate::Node;
pub struct FnNode<T: Fn(&In) -> O, In, O>(T, PhantomData<In>, PhantomData<O>);
impl<T: Fn(&In) -> O, In, O> Node for FnNode<T, In, O> {
type Output<'a> = O where Self: 'a;

View File

@ -1,16 +1,39 @@
#![feature(generic_associated_types)]
use std::{any::Any, borrow::Borrow};
pub mod generic;
pub mod ops;
pub mod structural;
pub mod value;
use std::{any::Any, borrow::Borrow, ops::Deref};
#[rustfmt::skip]
pub trait Node {
// Self: 'a means that Self has to live at least as long as 'a (the input and output)
// this ensures that the node does not spontaneously disappear during evaluation
type Output<'a> where Self: 'a;
type Input<'a> where Self: 'a;
type Input<'i> where Self: 'i;
type Output<'o> where Self: 'o;
fn eval<'a, I: Borrow<Self::Input<'a>>>(&'a self, input: I) -> Self::Output<'a>;
}
pub trait SimpleNode<'n, I, O> {
fn eval_simple(&self, input: &I) -> &O;
}
impl<T: for<'n> SimpleNode<'n, I, O>, I, O> Node for T {
type Input<'i> = &'i I where Self: 'i;
type Output<'o> = &'o O where Self: 'o;
fn eval<'a, In: Borrow<Self::Input<'a>>>(&'a self, input: In) -> Self::Output<'a> {
self.eval_simple(input.borrow())
}
}
#[rustfmt::skip]
pub trait OutputNode<'a, T>: Node<Output<'a> = T> where Self: 'a {}
#[rustfmt::skip]
pub trait ArgNode<'a, T>: OutputNode<'a, T> + Node<Input<'a> = ()> where Self: 'a {}
pub trait AnyRef: Node {
fn any<'a>(&'a self, input: &'a dyn Any) -> Self::Output<'a>
where
@ -33,6 +56,24 @@ impl<T: Node> AnyRef for T {
}
}
trait Ref<T>: Node {}
impl<'a, T: 'a, N: Node<Output<'a> = &'a T> + 'a> Ref<T> for N {}
pub trait ExecPtr<'n, T>: Node {
fn fn_ptr(&self) -> &T;
}
impl<'n, T: 'n, N: Ref<T>> ExecPtr<'n, T> for N
where
for<'a> &'a (): Borrow<<Self as Node>::Input<'a>>,
for<'a> &'a T: From<N::Output<'a>>,
{
fn fn_ptr(&self) -> &T {
let value: &T = self.eval(&()).into();
value
}
}
pub trait Exec: Node
where
for<'a> &'a (): Borrow<<Self as Node>::Input<'a>>,

View File

@ -1,6 +1,6 @@
use std::{borrow::Borrow, marker::PhantomData};
use graphene_core::Node;
use crate::Node;
#[derive(Default)]
pub struct AddNode<T>(PhantomData<T>);

View File

@ -1,6 +1,6 @@
use std::{any::Any, borrow::Borrow};
use graphene_core::{DynamicInput, Node};
use crate::{DynamicInput, Node};
pub struct ComposeNode<'n, FIRST, SECOND> {
first: &'n FIRST,
second: &'n SECOND,

View File

@ -0,0 +1,65 @@
use std::{borrow::Borrow, marker::PhantomData};
use const_default::ConstDefault;
use crate::{Exec, Node};
pub struct IntNode<const N: u32>;
impl<const N: u32> Node for IntNode<N> {
type Input<'o> = ();
type Output<'i> = u32;
fn eval<'a, I: Borrow<Self::Input<'a>>>(&self, _input: I) -> u32 {
N
}
}
#[derive(Default)]
pub struct ValueNode<T>(T);
impl<T> Node for ValueNode<T> {
type Input<'i> = () where T: 'i;
type Output<'o> = &'o T where T: 'o;
fn eval<'a, I: Borrow<Self::Input<'a>>>(&'a self, _input: I) -> &T {
&self.0
}
}
impl<T> ValueNode<T> {
pub const fn new(value: T) -> ValueNode<T> {
ValueNode(value)
}
}
#[derive(Default)]
pub struct DefaultNode<T>(PhantomData<T>);
impl<T: Default> Node for DefaultNode<T> {
type Input<'i> = () where T: 'i;
type Output<'o> = T where T: 'o;
fn eval<'a, I: Borrow<Self::Input<'a>>>(&'a self, _input: I) -> T {
T::default()
}
}
impl<T> DefaultNode<T> {
pub const fn new() -> DefaultNode<T> {
DefaultNode(PhantomData)
}
}
pub struct DefaultRefNode<T>(ValueNode<T>);
impl<T: 'static> Node for DefaultRefNode<T> {
type Input<'i> = () where T: 'i;
type Output<'o> = &'o T where T: 'o;
fn eval<'a, I: Borrow<Self::Input<'a>>>(&'a self, _input: I) -> &'a T {
self.0.exec()
}
}
#[cfg(feature = "const_default")]
impl<T: ConstDefault> DefaultRefNode<T> {
pub const fn new() -> DefaultRefNode<T> {
DefaultRefNode(ValueNode::new(T::DEFAULT))
}
}
#[cfg(not(feature = "const_default"))]
impl<T: Default> DefaultRefNode<T> {
pub fn new() -> DefaultRefNode<T> {
DefaultRefNode(ValueNode::new(T::default()))
}
}

View File

@ -2,9 +2,7 @@
#[cfg(feature = "caching")]
pub mod caching;
pub mod generic;
#[cfg(feature = "memoization")]
pub mod memo;
pub mod ops;
pub mod structural;
pub mod value;
pub use graphene_core::*;

View File

@ -1,7 +1,55 @@
use graphene_core::{Exec, Node};
#![feature(generic_associated_types)]
use graphene_std::*;
/*fn mul(a: f32, b: f32) -> f32 {
a * b
}*/
mod mul {
use graphene_std::{
value::DefaultNode, value::DefaultRefNode, ArgNode, DynamicInput, ExecPtr, Node,
};
use std::{any::Any, ops::Deref};
const A: DefaultRefNode<f32> = DefaultRefNode::new();
const B: DefaultRefNode<f32> = DefaultRefNode::new();
type F32Node<'n> = &'n dyn ExecPtr<'n, f32, Output<'n> = &'n f32, Input<'n> = ()>;
pub struct MulNode<'n> {
a: F32Node<'n>,
b: F32Node<'n>,
}
impl<'n> Node for MulNode<'n> {
type Input<'i> = () where Self: 'i;
type Output<'o> = f32 where Self: 'o;
fn eval<'a, I>(&'a self, input: I) -> <Self as graphene_std::Node>::Output<'a>
where
I: std::borrow::Borrow<Self::Input<'a>>,
{
let a = self.a.fn_ptr();
let b = self.b.fn_ptr();
a * b
}
}
impl<'n> MulNode<'n> {
pub const fn new() -> Self {
Self { a: &A, b: &B }
}
}
impl DynamicInput for MulNode<'_> {
fn set_kwarg_by_name(&mut self, _: &str, _: &(dyn std::any::Any + 'static)) {
todo!()
}
fn set_arg_by_index(&mut self, index: usize, input: &(dyn std::any::Any + 'static)) {
match index {
0 => self.a = input.downcast_ref::<&dyn ExecPtr<'_, f32>>().unwrap(),
_ => todo!(),
}
}
}
}
fn main() {
let mut mul = mul::MulNode::new();
let int = value::IntNode::<32>;
let _add: u32 = ops::AddNode::<u32>::default().eval((int.exec(), int.exec()));
let fnode = generic::FnNode::new(|(a, b): &(i32, i32)| a - b);

View File

@ -1,6 +1,6 @@
use graphene_core::Node;
use graphene_core::{ExecPtr, Node};
use once_cell::sync::OnceCell;
use std::borrow::Borrow;
use std::{any::Any, borrow::Borrow, ops::Deref};
/// Caches the output of a given Node and acts as a proxy
pub struct CacheNode<'n, 'c, CachedNode: Node + 'c> {

View File

@ -1,38 +0,0 @@
use std::borrow::Borrow;
use graphene_core::Node;
pub struct IntNode<const N: u32>;
impl<const N: u32> Node for IntNode<N> {
type Output<'a> = u32;
type Input<'a> = ();
fn eval<'a, I: Borrow<Self::Input<'a>>>(&self, _input: I) -> u32 {
N
}
}
#[derive(Default)]
pub struct ValueNode<T>(T);
impl<T> Node for ValueNode<T> {
type Output<'o> = &'o T where T: 'o;
type Input<'i> = () where T: 'i;
fn eval<'a, I: Borrow<Self::Input<'a>>>(&'a self, _input: I) -> &T {
&self.0
}
}
#[rustfmt::skip]
pub trait OutputNode<'a, T>: Node<Output<'a> = T> where Self: 'a {}
impl<T: std::default::Default> DefaultNode for T {}
impl<T> ValueNode<T> {
pub fn new(value: T) -> ValueNode<T> {
ValueNode(value)
}
}
pub trait DefaultNode: Default {
fn default_node() -> ValueNode<Self> {
ValueNode::new(Self::default())
}
}

View File

@ -1,28 +1,27 @@
use graphene_core;
use proc_macro::TokenStream;
use proc_macro_roids::*;
use quote::{quote, ToTokens};
use syn::punctuated::Punctuated;
use syn::{parse_macro_input, FnArg, ItemFn, Pat, Type};
fn extract_type(a: FnArg) -> Box<Type> {
fn extract_type(a: FnArg) -> Type {
match a {
FnArg::Typed(p) => p.ty, // notice `ty` instead of `pat`
FnArg::Typed(p) => *p.ty, // notice `ty` instead of `pat`
_ => panic!("Not supported on types with `self`!"),
}
}
fn extract_arg_types(fn_args: Punctuated<FnArg, syn::token::Comma>) -> Vec<Box<Type>> {
return fn_args.into_iter().map(extract_type).collect::<Vec<_>>();
fn extract_arg_types(fn_args: Punctuated<FnArg, syn::token::Comma>) -> Vec<Type> {
fn_args.into_iter().map(extract_type).collect::<Vec<_>>()
}
fn extract_arg_idents(fn_args: Punctuated<FnArg, syn::token::Comma>) -> Vec<Box<Pat>> {
return fn_args.into_iter().map(extract_arg_pat).collect::<Vec<_>>();
fn extract_arg_idents(fn_args: Punctuated<FnArg, syn::token::Comma>) -> Vec<Pat> {
fn_args.into_iter().map(extract_arg_pat).collect::<Vec<_>>()
}
fn extract_arg_pat(a: FnArg) -> Box<Pat> {
fn extract_arg_pat(a: FnArg) -> Pat {
match a {
FnArg::Typed(p) => p.pat,
FnArg::Typed(p) => *p.pat,
_ => panic!("Not supported on types with `self`!"),
}
}
@ -30,7 +29,7 @@ fn extract_arg_pat(a: FnArg) -> Box<Pat> {
#[proc_macro_attribute] // 2
pub fn to_node(_attr: TokenStream, item: TokenStream) -> TokenStream {
let string = item.to_string();
let item2 = item.clone();
let item2 = item;
let parsed = parse_macro_input!(item2 as ItemFn); // 3
//item.extend(generate_to_string(parsed, string)); // 4
//item
@ -56,9 +55,16 @@ fn generate_to_string(parsed: ItemFn, string: String) -> TokenStream {
.iter()
.map(|t| t.to_token_stream())
.collect::<Vec<_>>();
let const_idents = idents
.iter()
.map(|t| {
let name = t.to_string().to_uppercase();
quote! {#name}
})
.collect::<Vec<_>>();
let node_fn_name = fn_name.append("_node");
let struct_name = fn_name.append("_node");
let struct_name = fn_name.append("_input");
let return_type_string = fn_return_type
.to_token_stream()
.to_string()
@ -68,15 +74,18 @@ fn generate_to_string(parsed: ItemFn, string: String) -> TokenStream {
.map(|t| t.to_string())
.collect::<Vec<_>>()
.join(", ");
let error = format!("called {} with the wrong type", fn_name.to_string());
let error = format!("called {} with the wrong type", fn_name);
let x = quote! {
//#whole_function
mod #fn_name {
#(const #const_idents: DefaultNode<#types> = DefaultNode::new();)*
struct #struct_name {
#(#idents: #types,)*
}
impl Node for #struct_name {
}
impl
}
fn #node_fn_name #generics() -> Node<'static> {