44#define PY_SSIZE_T_CLEAN
55#include < Python.h>
66#include < string>
7+ #include < variant>
8+
9+ enum class DataType {
10+ None,
11+ Int,
12+ Double,
13+ String
14+ };
715
816typedef struct {
917 PyObject_HEAD
1018 std::string name;
11- PyObject* data;
19+ std::variant<std::monostate, int64_t , double , std::string> data;
20+ DataType data_type;
1221} GraphNode;
1322
1423static void GraphNode_dealloc (GraphNode* self){
15- Py_XDECREF (self->data );
1624 Py_TYPE (self)->tp_free (reinterpret_cast <PyTypeObject*>(self));
1725}
1826
1927static PyObject* GraphNode_new (PyTypeObject* type, PyObject* args, PyObject* kwds){
2028 GraphNode* self;
2129 self = reinterpret_cast <GraphNode*>(type->tp_alloc (type,0 ));
2230 new (&self->name ) std::string ();
31+ new (&self->data ) std::variant<std::monostate, int64_t , double , std::string>();
32+ self->data_type = DataType::None;
2333 if (!self) return NULL ;
2434
2535 static char * kwlist[] = { " name" , " data" , NULL };
@@ -32,54 +42,123 @@ static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwd
3242 }
3343
3444 self->name = std::string (name);
35- Py_INCREF (data);
36- self->data = data;
45+
46+ if (data == Py_None) {
47+ self->data = std::monostate{};
48+ self->data_type = DataType::None;
49+ } else if (PyLong_Check (data)) {
50+ self->data = static_cast <int64_t >(PyLong_AsLongLong (data));
51+ self->data_type = DataType::Int;
52+ } else if (PyFloat_Check (data)) {
53+ self->data = PyFloat_AsDouble (data);
54+ self->data_type = DataType::Double;
55+ } else if (PyUnicode_Check (data)) {
56+ const char * s = PyUnicode_AsUTF8 (data);
57+ self->data = std::string (s);
58+ self->data_type = DataType::String;
59+ } else {
60+ PyErr_SetString (PyExc_TypeError, " data must be int, float, str, or None" );
61+ return NULL ;
62+ }
3763
3864 return reinterpret_cast <PyObject*>(self);
3965}
4066
4167static PyObject* GraphNode_str (GraphNode* self) {
42- return PyUnicode_FromString ((" ('" + self->name + " ', " + PyUnicode_AsUTF8 (PyObject_Str (self->data )) + " )" ).c_str ());
68+ std::string repr = " ('" + self->name + " ', " ;
69+
70+ switch (self->data_type ) {
71+ case DataType::None:
72+ repr += " None" ;
73+ break ;
74+ case DataType::Int:
75+ repr += std::to_string (std::get<int64_t >(self->data ));
76+ break ;
77+ case DataType::Double:
78+ repr += std::to_string (std::get<double >(self->data ));
79+ break ;
80+ case DataType::String:
81+ repr += " '" + std::get<std::string>(self->data ) + " '" ;
82+ break ;
83+ }
84+ repr += " )" ;
85+ return PyUnicode_FromString (repr.c_str ());
4386}
4487
4588static PyObject* GraphNode_get (GraphNode* self, void *closure) {
4689 if (closure == (void *)" name" ) {
4790 return PyUnicode_FromString (self->name .c_str ());
48- }
49- if (closure == (void *)" data" ) {
50- Py_INCREF (self->data );
51- return self->data ;
91+ } else if (closure == (void *)" data" ) {
92+ switch (self->data_type ) {
93+ case DataType::None:
94+ Py_RETURN_NONE;
95+ case DataType::Int:
96+ return PyLong_FromLongLong (std::get<int64_t >(self->data ));
97+ case DataType::Double:
98+ return PyFloat_FromDouble (std::get<double >(self->data ));
99+ case DataType::String:
100+ return PyUnicode_FromString (std::get<std::string>(self->data ).c_str ());
101+ }
52102 }
53103 Py_RETURN_NONE;
54104}
55105
56106static int GraphNode_set (GraphNode* self, PyObject *value, void *closure) {
57- if (value == NULL ) {
58- PyErr_SetString (PyExc_ValueError, " value is NULL " );
107+ if (! value) {
108+ PyErr_SetString (PyExc_ValueError, " Cannot delete attributes " );
59109 return -1 ;
60110 }
61111
62112 if (closure == (void *)" name" ) {
63113 if (!PyUnicode_Check (value)) {
64- PyErr_SetString (PyExc_TypeError, " value to be set must be a string" );
114+ PyErr_SetString (PyExc_TypeError, " name must be a string" );
65115 return -1 ;
66116 }
67117 self->name = PyUnicode_AsUTF8 (value);
68118 }
69119 else if (closure == (void *)" data" ) {
70- PyObject *tmp = self->data ;
71- Py_INCREF (value);
72- self->data = value;
73- Py_DECREF (tmp);
74- }
75- else {
120+ if (value == Py_None) {
121+ self->data = std::monostate{};
122+ self->data_type = DataType::None;
123+ } else if (PyLong_Check (value)) {
124+ self->data = static_cast <int64_t >(PyLong_AsLongLong (value));
125+ self->data_type = DataType::Int;
126+ } else if (PyFloat_Check (value)) {
127+ self->data = PyFloat_AsDouble (value);
128+ self->data_type = DataType::Double;
129+ } else if (PyUnicode_Check (value)) {
130+ self->data = std::string (PyUnicode_AsUTF8 (value));
131+ self->data_type = DataType::String;
132+ } else {
133+ PyErr_SetString (PyExc_TypeError, " data must be int, float, str, or None" );
134+ return -1 ;
135+ }
136+ } else {
76137 PyErr_SetString (PyExc_AttributeError, " Unknown attribute" );
77138 return -1 ;
78139 }
79140
80141 return 0 ;
81142}
82143
144+ static PyGetSetDef GraphNode_getsetters[] = {
145+ {
146+ const_cast <char *>(" name" ),
147+ reinterpret_cast <getter>(GraphNode_get),
148+ reinterpret_cast <setter>(GraphNode_set),
149+ const_cast <char *>(" name" ),
150+ reinterpret_cast <void *>(const_cast <char *>(" name" ))
151+ },
152+ {
153+ const_cast <char *>(" data" ),
154+ reinterpret_cast <getter>(GraphNode_get),
155+ reinterpret_cast <setter>(GraphNode_set),
156+ const_cast <char *>(" data" ),
157+ reinterpret_cast <void *>(const_cast <char *>(" data" ))
158+ },
159+ {nullptr }
160+ };
161+
83162static PyTypeObject GraphNodeType = {
84163 /* tp_name */ PyVarObject_HEAD_INIT (NULL , 0 ) " GraphNode" ,
85164 /* tp_basicsize */ sizeof (GraphNode),
@@ -109,7 +188,7 @@ static PyTypeObject GraphNodeType = {
109188 /* tp_iternext */ 0 ,
110189 /* tp_methods */ 0 ,
111190 /* tp_members */ 0 ,
112- /* tp_getset */ 0 ,
191+ /* tp_getset */ GraphNode_getsetters ,
113192 /* tp_base */ &PyBaseObject_Type,
114193 /* tp_dict */ 0 ,
115194 /* tp_descr_get */ 0 ,
0 commit comments