4
4
#define PY_SSIZE_T_CLEAN
5
5
#include < Python.h>
6
6
#include < string>
7
+ #include < variant>
8
+
9
+ enum class DataType {
10
+ None,
11
+ Int,
12
+ Double,
13
+ String
14
+ };
7
15
8
16
typedef struct {
9
17
PyObject_HEAD
10
18
std::string name;
11
- PyObject* data;
19
+ std::variant<std::monostate, int64_t , double , std::string> data;
20
+ DataType data_type;
12
21
} GraphNode;
13
22
14
23
static void GraphNode_dealloc (GraphNode* self){
15
- Py_XDECREF (self->data );
16
24
Py_TYPE (self)->tp_free (reinterpret_cast <PyTypeObject*>(self));
17
25
}
18
26
19
27
static PyObject* GraphNode_new (PyTypeObject* type, PyObject* args, PyObject* kwds){
20
28
GraphNode* self;
21
29
self = reinterpret_cast <GraphNode*>(type->tp_alloc (type,0 ));
22
30
new (&self->name ) std::string ();
31
+ new (&self->data ) std::variant<std::monostate, int64_t , double , std::string>();
32
+ self->data_type = DataType::None;
23
33
if (!self) return NULL ;
24
34
25
35
static char * kwlist[] = { " name" , " data" , NULL };
@@ -32,54 +42,123 @@ static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwd
32
42
}
33
43
34
44
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
+ }
37
63
38
64
return reinterpret_cast <PyObject*>(self);
39
65
}
40
66
41
67
static 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 ());
43
86
}
44
87
45
88
static PyObject* GraphNode_get (GraphNode* self, void *closure) {
46
89
if (closure == (void *)" name" ) {
47
90
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
+ }
52
102
}
53
103
Py_RETURN_NONE;
54
104
}
55
105
56
106
static 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 " );
59
109
return -1 ;
60
110
}
61
111
62
112
if (closure == (void *)" name" ) {
63
113
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" );
65
115
return -1 ;
66
116
}
67
117
self->name = PyUnicode_AsUTF8 (value);
68
118
}
69
119
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 {
76
137
PyErr_SetString (PyExc_AttributeError, " Unknown attribute" );
77
138
return -1 ;
78
139
}
79
140
80
141
return 0 ;
81
142
}
82
143
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
+
83
162
static PyTypeObject GraphNodeType = {
84
163
/* tp_name */ PyVarObject_HEAD_INIT (NULL , 0 ) " GraphNode" ,
85
164
/* tp_basicsize */ sizeof (GraphNode),
@@ -109,7 +188,7 @@ static PyTypeObject GraphNodeType = {
109
188
/* tp_iternext */ 0 ,
110
189
/* tp_methods */ 0 ,
111
190
/* tp_members */ 0 ,
112
- /* tp_getset */ 0 ,
191
+ /* tp_getset */ GraphNode_getsetters ,
113
192
/* tp_base */ &PyBaseObject_Type,
114
193
/* tp_dict */ 0 ,
115
194
/* tp_descr_get */ 0 ,
0 commit comments