44#define PY_SSIZE_T_CLEAN
55#include < Python.h>
66#include < string>
7+ #include < variant>
78#include " GraphNode.hpp"
89
910extern PyTypeObject GraphEdgeType;
@@ -12,56 +13,123 @@ typedef struct {
1213 PyObject_HEAD
1314 PyObject* source;
1415 PyObject* target;
15- PyObject* value;
16+ std::variant<std::monostate, int64_t , double , std::string> value;
17+ DataType value_type;
1618} GraphEdge;
1719
1820static void GraphEdge_dealloc (GraphEdge* self) {
1921 Py_XDECREF (self->source );
2022 Py_XDECREF (self->target );
21- Py_XDECREF (self->value );
2223 Py_TYPE (self)->tp_free (reinterpret_cast <PyObject*>(self));
2324}
2425
2526static PyObject* GraphEdge_new (PyTypeObject* type, PyObject* args, PyObject* kwds) {
26- GraphEdge* self;
27- self = reinterpret_cast <GraphEdge*>(type->tp_alloc (type, 0 ));
27+ GraphEdge* self = PyObject_New (GraphEdge, &GraphEdgeType);
2828 if (!self) return NULL ;
2929
30+ new (&self->value ) std::variant<std::monostate, int64_t , double , std::string>();
31+ self->value_type = DataType::None;
32+
3033 static char * kwlist[] = {" node1" , " node2" , " value" , NULL };
3134 PyObject* node1;
3235 PyObject* node2;
3336 PyObject* value = Py_None;
3437
3538 if (!PyArg_ParseTupleAndKeywords (args, kwds, " OO|O" , kwlist, &node1, &node2, &value)) {
36- PyErr_SetString (PyExc_ValueError, " Invalid arguments: Expected (GraphNode, GraphNode, optional value)" );
39+ PyErr_SetString (PyExc_ValueError, " Expected (GraphNode, GraphNode, optional value)" );
3740 return NULL ;
3841 }
3942
4043 Py_INCREF (node1);
4144 Py_INCREF (node2);
42- Py_INCREF (value);
43-
4445 self->source = node1;
4546 self->target = node2;
46- self->value = value;
47+
48+ if (value == Py_None) {
49+ self->value_type = DataType::None;
50+ self->value = std::monostate{};
51+ } else if (PyLong_Check (value)) {
52+ self->value_type = DataType::Int;
53+ self->value = static_cast <int64_t >(PyLong_AsLongLong (value));
54+ } else if (PyFloat_Check (value)) {
55+ self->value_type = DataType::Double;
56+ self->value = PyFloat_AsDouble (value);
57+ } else if (PyUnicode_Check (value)) {
58+ const char * str = PyUnicode_AsUTF8 (value);
59+ self->value_type = DataType::String;
60+ self->value = std::string (str);
61+ } else {
62+ PyErr_SetString (PyExc_TypeError, " Unsupported edge value type (must be int, float, str, or None)" );
63+ return NULL ;
64+ }
4765
4866 return reinterpret_cast <PyObject*>(self);
4967}
5068
5169static PyObject* GraphEdge_str (GraphEdge* self) {
52- std::string source_name = (reinterpret_cast <GraphNode*>(self->source ))->name ;
53- std::string target_name = (reinterpret_cast <GraphNode*>(self->target ))->name ;
54-
55- if (source_name.empty () || target_name.empty ()) {
56- PyErr_SetString (PyExc_AttributeError, " Both nodes must have a 'name' attribute." );
57- return NULL ;
70+ std::string src = reinterpret_cast <GraphNode*>(self->source )->name ;
71+ std::string tgt = reinterpret_cast <GraphNode*>(self->target )->name ;
72+ std::string val_str;
73+
74+ switch (self->value_type ) {
75+ case DataType::Int:
76+ val_str = std::to_string (std::get<int64_t >(self->value ));
77+ break ;
78+ case DataType::Double:
79+ val_str = std::to_string (std::get<double >(self->value ));
80+ break ;
81+ case DataType::String:
82+ val_str = std::get<std::string>(self->value );
83+ break ;
84+ case DataType::None:
85+ default :
86+ val_str = " None" ;
87+ break ;
5888 }
5989
60- PyObject* str_repr = PyUnicode_FromFormat (" ('%s', '%s')" , source_name.c_str (), target_name.c_str ());
90+ return PyUnicode_FromFormat (" ('%s', '%s', %s)" , src.c_str (), tgt.c_str (), val_str.c_str ());
91+ }
6192
62- return str_repr;
93+ static PyObject* GraphEdge_get_value (GraphEdge* self, void * closure) {
94+ switch (self->value_type ) {
95+ case DataType::Int:
96+ return PyLong_FromLongLong (std::get<int64_t >(self->value ));
97+ case DataType::Double:
98+ return PyFloat_FromDouble (std::get<double >(self->value ));
99+ case DataType::String:
100+ return PyUnicode_FromString (std::get<std::string>(self->value ).c_str ());
101+ case DataType::None:
102+ default :
103+ Py_RETURN_NONE;
104+ }
63105}
64106
107+ static int GraphEdge_set_value (GraphEdge* self, PyObject* value) {
108+ if (value == Py_None) {
109+ self->value_type = DataType::None;
110+ self->value = std::monostate{};
111+ } else if (PyLong_Check (value)) {
112+ self->value_type = DataType::Int;
113+ self->value = static_cast <int64_t >(PyLong_AsLongLong (value));
114+ } else if (PyFloat_Check (value)) {
115+ self->value_type = DataType::Double;
116+ self->value = PyFloat_AsDouble (value);
117+ } else if (PyUnicode_Check (value)) {
118+ const char * str = PyUnicode_AsUTF8 (value);
119+ self->value_type = DataType::String;
120+ self->value = std::string (str);
121+ } else {
122+ PyErr_SetString (PyExc_TypeError, " Edge value must be int, float, str, or None." );
123+ return -1 ;
124+ }
125+ return 0 ;
126+ }
127+
128+ static PyGetSetDef GraphEdge_getsetters[] = {
129+ {" value" , (getter)GraphEdge_get_value, (setter)GraphEdge_set_value, " Get or set edge value" , NULL },
130+ {NULL }
131+ };
132+
65133inline PyTypeObject GraphEdgeType = {
66134 /* tp_name */ PyVarObject_HEAD_INIT (NULL , 0 ) " GraphEdge" ,
67135 /* tp_basicsize */ sizeof (GraphEdge),
0 commit comments