Merge lp:~posulliv/libmemcached/c++-interface into lp:~tangent-org/libmemcached/trunk

Proposed by Padraig O'Sullivan
Status: Merged
Merged at revision: not available
Proposed branch: lp:~posulliv/libmemcached/c++-interface
Merge into: lp:~tangent-org/libmemcached/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~posulliv/libmemcached/c++-interface
Reviewer Review Type Date Requested Status
Libmemcached-developers Pending
Review via email: mp+8617@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Padraig O'Sullivan (posulliv) wrote :

So I was thinking that when we actually port the memcached UDF's to Drizzle, we should use the libmemcached C++ interface since Drizzle is a C++ project. Thus, to enable that, I did some refactoring of the libmemcached C++ interface wrapper.

Basically, all I did was the following:

* replace parameters as char * with references to std::string
* use std::vector in places where arrays of pointers were being passed
* modified the return types of numerous API function to be bool instead of memcached_return
* added more test cases to the C++ API test file

I'm just proposing this for merging to get some feedback on the approach I've taken. I can easily keep the existing interface by just overloading those API functions if that is wanted.

-Padraig

Revision history for this message
Brian Aker (brianaker) wrote :

Thanks!

This is a huge improvement over the current interface (which I am sure no one is using).

Revision history for this message
Brian Aker (brianaker) wrote :

Looking through the code.. mget() going to be quite slow I suspect because of the memory allocation that will be done because of it.

I modified mget() to not do a malloc, and instead use a vector for the key sizes.

I suspect we could look at the lower level interfaces and find a way of getting the advantages of mget() without doing the vector overhead. Probably just a matter of flipping the bit for pipelining requests and sending down directly the bits from the std:string.

Code is now in trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'libmemcached/memcached.hh'
--- libmemcached/memcached.hh 2009-07-07 21:57:24 +0000
+++ libmemcached/memcached.hh 2009-07-11 19:10:39 +0000
@@ -1,175 +1,354 @@
1#include "libmemcached/memcached.h"1#include <libmemcached/memcached.h>
2#include <string.h>2
3#include <stdio.h>3#include <string>
4#include <vector>
45
5class Memcached6class Memcached
6{7{
7 memcached_st memc;
8 memcached_result_st result;
9
10public:8public:
119
12 Memcached() : memc(), result()10 Memcached()
11 :
12 memc(),
13 result()
13 {14 {
14 memcached_create(&memc);15 memcached_create(&memc);
15 }16 }
1617
17 Memcached(memcached_st *clone) : memc(), result()18 Memcached(memcached_st *clone)
19 :
20 memc(),
21 result()
18 {22 {
19 memcached_clone(&memc, clone);23 memcached_clone(&memc, clone);
20 }24 }
21 char *fetch (char *key, size_t *key_length, size_t *value_length)25
22 {
23 uint32_t flags;
24 memcached_return rc;
25
26 return memcached_fetch(&memc, key, key_length,
27 value_length, &flags, &rc);
28 }
29 char *get(const char *key, size_t *value_length)
30 {
31 uint32_t flags;
32 memcached_return rc;
33
34 return memcached_get(&memc, key, strlen(key),
35 value_length, &flags, &rc);
36 }
37
38 char *get_by_key(const char *master_key, const char *key,
39 size_t *value_length)
40 {
41 uint32_t flags;
42 memcached_return rc;
43
44 return memcached_get_by_key(&memc, master_key, strlen(master_key),
45 key, strlen(key),
46 value_length, &flags, &rc);
47 }
48
49 memcached_return mget(char **keys, size_t *key_length,
50 unsigned int number_of_keys)
51 {
52
53 return memcached_mget(&memc, keys, key_length, number_of_keys);
54 }
55
56 memcached_return set(const char *key, const char *value, size_t value_length)
57 {
58 return memcached_set(&memc, key, strlen(key),
59 value, value_length,
60 time_t(0), uint32_t(0));
61 }
62
63 memcached_return set_by_key(const char *master_key, const char *key,
64 const char *value, size_t value_length)
65 {
66 return memcached_set_by_key(&memc, master_key, strlen(master_key),
67 key, strlen(key),
68 value, value_length,
69 time_t(0),
70 uint32_t(0) );
71 }
72 memcached_return
73 increment(const char *key, unsigned int offset, uint64_t *value)
74 {
75 return memcached_increment(&memc, key, strlen(key),
76 offset, value);
77 }
78 memcached_return
79 decrement(const char *key, unsigned int offset, uint64_t *value)
80 {
81 return memcached_decrement(&memc, key, strlen(key),
82 offset, value);
83 }
84
85
86 memcached_return add(const char *key, const char *value, size_t value_length)
87 {
88 return memcached_add(&memc, key, strlen(key), value, value_length, 0, 0);
89 }
90 memcached_return add_by_key(const char *master_key, const char *key,
91 const char *value, size_t value_length)
92 {
93 return memcached_add_by_key(&memc, master_key, strlen(master_key),
94 key, strlen(key),
95 value, value_length,
96 0, 0);
97 }
98
99 memcached_return replace(const char *key, const char *value,
100 size_t value_length)
101 {
102 return memcached_replace(&memc, key, strlen(key),
103 value, value_length,
104 0, 0);
105 }
106 memcached_return replace_by_key(const char *master_key, const char *key,
107 const char *value, size_t value_length)
108 {
109 return memcached_replace_by_key(&memc, master_key, strlen(master_key),
110 key, strlen(key),
111 value, value_length, 0, 0);
112 }
113
114 memcached_return prepend(const char *key, const char *value,
115 size_t value_length)
116 {
117 return memcached_prepend(&memc, key, strlen(key),
118 value, value_length, 0, 0);
119 }
120 memcached_return prepend_by_key(const char *master_key, const char *key,
121 const char *value, size_t value_length)
122 {
123 return memcached_prepend_by_key(&memc, master_key, strlen(master_key),
124 key, strlen(key),
125 value, value_length,
126 0,
127 0);
128 }
129
130 memcached_return append(const char *key, const char *value,
131 size_t value_length)
132 {
133 return memcached_append(&memc, key, strlen(key),
134 value, value_length, 0, 0);
135 }
136 memcached_return append_by_key(const char *master_key, const char *key,
137 const char *value, size_t value_length)
138 {
139 return memcached_append_by_key(&memc,
140 master_key, strlen(master_key),
141 key, strlen(key),
142 value, value_length, 0, 0);
143 }
144 memcached_return cas(const char *key, const char *value,
145 size_t value_length, uint64_t cas_arg)
146 {
147 return memcached_cas(&memc, key, strlen(key),
148 value, value_length, 0, 0, cas_arg);
149 }
150 memcached_return cas_by_key(const char *master_key, const char *key,
151 const char *value, size_t value_length,
152 uint64_t cas_arg)
153 {
154 return memcached_cas_by_key(&memc,
155 master_key, strlen(master_key),
156 key, strlen(key),
157 value, value_length,
158 0, 0, cas_arg);
159 }
160 // using 'remove' vs. 'delete' since 'delete' is a keyword
161 memcached_return remove(const char *key)
162 {
163 return memcached_delete (&memc, key, strlen(key), 0);
164
165 }
166 memcached_return delete_by_key(const char *master_key, const char *key)
167 {
168 return memcached_delete_by_key(&memc, master_key, strlen(master_key),
169 key, strlen(key), 0);
170 }
171 ~Memcached()26 ~Memcached()
172 {27 {
173 memcached_free(&memc);28 memcached_free(&memc);
174 }29 }
30
31 bool fetch(std::string &key,
32 std::string &ret_val,
33 size_t *key_length,
34 size_t *value_length,
35 uint32_t *flags,
36 memcached_return *rc)
37 {
38 char ret_key[MEMCACHED_MAX_KEY];
39 char *value= memcached_fetch(&memc, ret_key, key_length,
40 value_length, flags, rc);
41 if (value)
42 {
43 ret_val.assign(value);
44 key.assign(ret_key);
45 return true;
46 }
47 return false;
48 }
49
50 std::string get(const std::string &key, size_t *value_length)
51 {
52 uint32_t flags;
53 memcached_return rc;
54 std::string ret_val;
55
56 char *value= memcached_get(&memc, key.c_str(), key.length(),
57 value_length, &flags, &rc);
58 if (value)
59 {
60 ret_val.assign(value);
61 }
62 return ret_val;
63 }
64
65 std::string get_by_key(const std::string &master_key,
66 const std::string &key,
67 size_t *value_length)
68 {
69 uint32_t flags;
70 memcached_return rc;
71 std::string ret_val;
72
73 char *value= memcached_get_by_key(&memc, master_key.c_str(), master_key.length(),
74 key.c_str(), key.length(),
75 value_length, &flags, &rc);
76 if (value)
77 {
78 ret_val.assign(value);
79 }
80 return ret_val;
81 }
82
83 bool mget(std::vector<std::string> &keys)
84 {
85 /*
86 * Construct an array which will contain the length
87 * of each of the strings in the input vector. Also, to
88 * interface with the memcached C API, we need to convert
89 * the vector of std::string's to a vector of char *.
90 */
91 size_t *key_len= static_cast<size_t *>(malloc(keys.size() * sizeof(size_t)));
92 if (key_len == NULL)
93 {
94 return false;
95 }
96 std::vector<char *> real_keys;
97 std::vector<std::string>::iterator it= keys.begin();
98 int i= 0;
99 while (it != keys.end())
100 {
101 real_keys.push_back(const_cast<char *>((*it).c_str()));
102 key_len[i++]= (*it).length();
103 ++it;
104 }
105
106 /*
107 * If the std::vector of keys is empty then we cannot
108 * call memcached_mget as we will get undefined behavior.
109 */
110 if (!real_keys.empty())
111 {
112 memcached_return rc= memcached_mget(&memc, &real_keys[0], key_len,
113 static_cast<unsigned int>(real_keys.size()));
114 return (rc == MEMCACHED_SUCCESS);
115 }
116
117 return false;
118 }
119
120 bool set(const std::string &key,
121 const std::string &value,
122 time_t expiration,
123 uint32_t flags)
124 {
125 memcached_return rc= memcached_set(&memc,
126 key.c_str(), key.length(),
127 value.c_str(), value.length(),
128 expiration, flags);
129 return (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
130 }
131
132 bool set_all(std::vector<std::string> &keys,
133 std::vector<std::string> &values,
134 time_t expiration,
135 uint32_t flags)
136 {
137 if (keys.size() != values.size())
138 {
139 return false;
140 }
141 bool retval= true;
142 std::vector<std::string>::iterator key_it= keys.begin();
143 std::vector<std::string>::iterator val_it= values.begin();
144 while (key_it != keys.end())
145 {
146 retval= set((*key_it), (*val_it), expiration, flags);
147 if (retval == false)
148 {
149 return retval;
150 }
151 ++key_it;
152 ++val_it;
153 }
154 return retval;
155 }
156
157 bool set_by_key(const std::string &master_key,
158 const std::string &key,
159 const std::string &value,
160 time_t expiration,
161 uint32_t flags)
162 {
163 memcached_return rc= memcached_set_by_key(&memc, master_key.c_str(),
164 master_key.length(),
165 key.c_str(), key.length(),
166 value.c_str(), value.length(),
167 expiration,
168 flags);
169 return (rc == MEMCACHED_SUCCESS);
170 }
171
172 bool increment(const std::string &key, unsigned int offset, uint64_t *value)
173 {
174 memcached_return rc= memcached_increment(&memc, key.c_str(), key.length(),
175 offset, value);
176 return (rc == MEMCACHED_SUCCESS);
177 }
178
179 bool decrement(const std::string &key, unsigned int offset, uint64_t *value)
180 {
181 memcached_return rc= memcached_decrement(&memc, key.c_str(),
182 key.length(),
183 offset, value);
184 return (rc == MEMCACHED_SUCCESS);
185 }
186
187
188 bool add(const std::string &key, const std::string &value)
189 {
190 memcached_return rc= memcached_add(&memc, key.c_str(), key.length(),
191 value.c_str(), value.length(), 0, 0);
192 return (rc == MEMCACHED_SUCCESS);
193 }
194
195 bool add_by_key(const std::string &master_key,
196 const std::string &key,
197 const std::string &value)
198 {
199 memcached_return rc= memcached_add_by_key(&memc,
200 master_key.c_str(),
201 master_key.length(),
202 key.c_str(),
203 key.length(),
204 value.c_str(),
205 value.length(),
206 0, 0);
207 return (rc == MEMCACHED_SUCCESS);
208 }
209
210 bool replace(const std::string &key, const std::string &value)
211 {
212 memcached_return rc= memcached_replace(&memc, key.c_str(), key.length(),
213 value.c_str(), value.length(),
214 0, 0);
215 return (rc == MEMCACHED_SUCCESS);
216 }
217
218 bool replace_by_key(const std::string &master_key,
219 const std::string &key,
220 const std::string &value)
221 {
222 memcached_return rc= memcached_replace_by_key(&memc,
223 master_key.c_str(),
224 master_key.length(),
225 key.c_str(),
226 key.length(),
227 value.c_str(),
228 value.length(),
229 0, 0);
230 return (rc == MEMCACHED_SUCCESS);
231 }
232
233 bool prepend(const std::string &key, const std::string &value)
234 {
235 memcached_return rc= memcached_prepend(&memc, key.c_str(), key.length(),
236 value.c_str(), value.length(), 0, 0);
237 return (rc == MEMCACHED_SUCCESS);
238 }
239
240 bool prepend_by_key(const std::string &master_key,
241 const std::string &key,
242 const std::string &value)
243 {
244 memcached_return rc= memcached_prepend_by_key(&memc,
245 master_key.c_str(),
246 master_key.length(),
247 key.c_str(),
248 key.length(),
249 value.c_str(),
250 value.length(),
251 0,
252 0);
253 return (rc == MEMCACHED_SUCCESS);
254 }
255
256 bool append(const std::string &key, const std::string &value)
257 {
258 memcached_return rc= memcached_append(&memc,
259 key.c_str(),
260 key.length(),
261 value.c_str(),
262 value.length(),
263 0, 0);
264 return (rc == MEMCACHED_SUCCESS);
265 }
266
267 bool append_by_key(const std::string &master_key,
268 const std::string &key,
269 const std::string &value)
270 {
271 memcached_return rc= memcached_append_by_key(&memc,
272 master_key.c_str(),
273 master_key.length(),
274 key.c_str(),
275 key.length(),
276 value.c_str(),
277 value.length(),
278 0, 0);
279 return (rc == MEMCACHED_SUCCESS);
280 }
281
282 bool cas(const std::string &key,
283 const std::string &value,
284 uint64_t cas_arg)
285 {
286 memcached_return rc= memcached_cas(&memc, key.c_str(), key.length(),
287 value.c_str(), value.length(),
288 0, 0, cas_arg);
289 return (rc == MEMCACHED_SUCCESS);
290 }
291
292 bool cas_by_key(const std::string &master_key,
293 const std::string &key,
294 const std::string &value,
295 uint64_t cas_arg)
296 {
297 memcached_return rc= memcached_cas_by_key(&memc,
298 master_key.c_str(),
299 master_key.length(),
300 key.c_str(),
301 key.length(),
302 value.c_str(),
303 value.length(),
304 0, 0, cas_arg);
305 return (rc == MEMCACHED_SUCCESS);
306 }
307
308 // using 'remove' vs. 'delete' since 'delete' is a keyword
309 bool remove(const std::string &key)
310 {
311 memcached_return rc= memcached_delete(&memc, key.c_str(), key.length(), 0);
312 return (rc == MEMCACHED_SUCCESS);
313 }
314
315 bool delete_by_key(const std::string &master_key,
316 const std::string &key)
317 {
318 memcached_return rc= memcached_delete_by_key(&memc,
319 master_key.c_str(),
320 master_key.length(),
321 key.c_str(),
322 key.length(),
323 0);
324 return (rc == MEMCACHED_SUCCESS);
325 }
326
327 bool flush(time_t expiration)
328 {
329 memcached_return rc= memcached_flush(&memc, expiration);
330 return (rc == MEMCACHED_SUCCESS);
331 }
332
333 bool fetch_execute(memcached_execute_function *callback,
334 void *context,
335 unsigned int num_of_callbacks)
336 {
337 memcached_return rc= memcached_fetch_execute(&memc,
338 callback,
339 context,
340 num_of_callbacks);
341 return (rc == MEMCACHED_SUCCESS);
342 }
343
344 const std::string lib_version() const
345 {
346 const char *ver= memcached_lib_version();
347 const std::string version(ver);
348 return version;
349 }
350
351private:
352 memcached_st memc;
353 memcached_result_st result;
175};354};
176355
=== modified file 'tests/plus.cpp'
--- tests/plus.cpp 2009-07-07 21:57:24 +0000
+++ tests/plus.cpp 2009-07-11 19:10:39 +0000
@@ -16,87 +16,184 @@
1616
17#include "test.h"17#include "test.h"
1818
19#include <string>
20
21using namespace std;
22
19extern "C" {23extern "C" {
20 test_return basic_test(memcached_st *memc);
21 uint8_t increment_test(memcached_st *memc);
22 test_return basic_master_key_test(memcached_st *memc);
23 void *world_create(void);24 void *world_create(void);
24 void world_destroy(void *p);25 void world_destroy(void *p);
25}26}
2627
27test_return basic_test(memcached_st *memc)28static test_return basic_test(memcached_st *memc)
28{29{
29 Memcached foo(memc);30 Memcached foo(memc);
30 const char *value_set= "This is some data";31 const string value_set("This is some data");
31 char *value;32 string value;
32 size_t value_length;33 size_t value_length;
3334
34 foo.set("mine", value_set, strlen(value_set));35 foo.set("mine", value_set, 0, 0);
35 value= foo.get("mine", &value_length);36 value= foo.get("mine", &value_length);
3637
37 assert((memcmp(value, value_set, value_length) == 0));38 assert((memcmp(value.c_str(), value_set.c_str(), value_length) == 0));
3839
39 return TEST_SUCCESS;40 return TEST_SUCCESS;
40}41}
4142
42uint8_t increment_test(memcached_st *memc)43static test_return increment_test(memcached_st *memc)
43{44{
44 Memcached mcach(memc);45 Memcached mcach(memc);
45 memcached_return rc;46 bool rc;
46 const char *key= "inctest";47 const string key("inctest");
47 const char *inc_value= "1";48 const string inc_value("1");
48 char *ret_value;49 string ret_value;
49 uint64_t int_inc_value;50 uint64_t int_inc_value;
50 uint64_t int_ret_value;51 uint64_t int_ret_value;
51 size_t value_length;52 size_t value_length;
5253
53 mcach.set(key, inc_value, strlen(inc_value));54 mcach.set(key, inc_value, 0, 0);
54 ret_value= mcach.get(key, &value_length);55 ret_value= mcach.get(key, &value_length);
55 printf("\nretvalue %s\n",ret_value);56 printf("\nretvalue %s\n",ret_value.c_str());
56 int_inc_value= uint64_t(atol(inc_value));57 int_inc_value= uint64_t(atol(inc_value.c_str()));
57 int_ret_value= uint64_t(atol(ret_value));58 int_ret_value= uint64_t(atol(ret_value.c_str()));
58 assert(int_ret_value == int_inc_value); 59 assert(int_ret_value == int_inc_value);
5960
60 rc= mcach.increment(key, 1, &int_ret_value);61 rc= mcach.increment(key, 1, &int_ret_value);
61 assert(rc == MEMCACHED_SUCCESS);62 assert(rc == true);
62 assert(int_ret_value == 2);63 assert(int_ret_value == 2);
6364
64 rc= mcach.increment(key, 1, &int_ret_value);65 rc= mcach.increment(key, 1, &int_ret_value);
65 assert(rc == MEMCACHED_SUCCESS);66 assert(rc == true);
66 assert(int_ret_value == 3);67 assert(int_ret_value == 3);
6768
68 rc= mcach.increment(key, 5, &int_ret_value);69 rc= mcach.increment(key, 5, &int_ret_value);
69 assert(rc == MEMCACHED_SUCCESS);70 assert(rc == true);
70 assert(int_ret_value == 8);71 assert(int_ret_value == 8);
7172
72 return 0;73 return TEST_SUCCESS;
73}74}
7475
75test_return basic_master_key_test(memcached_st *memc)76static test_return basic_master_key_test(memcached_st *memc)
76{77{
77 Memcached foo(memc);78 Memcached foo(memc);
78 const char *value_set= "Data for server A";79 const string value_set("Data for server A");
79 const char *master_key_a= "server-a";80 const string master_key_a("server-a");
80 const char *master_key_b= "server-b";81 const string master_key_b("server-b");
81 const char *key= "xyz";82 const string key("xyz");
82 char *value;83 string value;
83 size_t value_length;84 size_t value_length;
8485
85 foo.set_by_key(master_key_a, key, value_set, strlen(value_set));86 foo.set_by_key(master_key_a, key, value_set, 0, 0);
86 value= foo.get_by_key(master_key_a, key, &value_length);87 value= foo.get_by_key(master_key_a, key, &value_length);
8788
88 assert((memcmp(value, value_set, value_length) == 0));89 assert((memcmp(value.c_str(), value_set.c_str(), value_length) == 0));
8990
90 value= foo.get_by_key(master_key_b, key, &value_length);91 value= foo.get_by_key(master_key_b, key, &value_length);
91 assert((memcmp(value, value_set, value_length) == 0));92 assert((memcmp(value.c_str(), value_set.c_str(), value_length) == 0));
9293
93 return TEST_SUCCESS;94 return TEST_SUCCESS;
94}95}
9596
97/* Count the results */
98static memcached_return callback_counter(memcached_st *ptr __attribute__((unused)),
99 memcached_result_st *result __attribute__((unused)),
100 void *context)
101{
102 unsigned int *counter= static_cast<unsigned int *>(context);
103
104 *counter= *counter + 1;
105
106 return MEMCACHED_SUCCESS;
107}
108
109static test_return mget_result_function(memcached_st *memc)
110{
111 Memcached mc(memc);
112 bool rc;
113 string key1("fudge");
114 string key2("son");
115 string key3("food");
116 vector<string> keys;
117 keys.reserve(3);
118 keys.push_back(key1);
119 keys.push_back(key2);
120 keys.push_back(key3);
121 unsigned int counter;
122 memcached_execute_function callbacks[1];
123
124 /* We need to empty the server before we continue the test */
125 rc= mc.flush(0);
126 rc= mc.set_all(keys, keys, 50, 9);
127 assert(rc == true);
128
129 rc= mc.mget(keys);
130 assert(rc == true);
131
132 callbacks[0]= &callback_counter;
133 counter= 0;
134 rc= mc.fetch_execute(callbacks, static_cast<void *>(&counter), 1);
135
136 assert(counter == 3);
137
138 return TEST_SUCCESS;
139}
140
141static test_return mget_test(memcached_st *memc)
142{
143 Memcached mc(memc);
144 bool rc;
145 memcached_return mc_rc;
146 vector<string> keys;
147 keys.reserve(3);
148 keys.push_back("fudge");
149 keys.push_back("son");
150 keys.push_back("food");
151 uint32_t flags;
152
153 string return_key;
154 size_t return_key_length;
155 string return_value;
156 size_t return_value_length;
157
158 /* We need to empty the server before we continue the test */
159 rc= mc.flush(0);
160 assert(rc == true);
161
162 rc= mc.mget(keys);
163 assert(rc == true);
164
165 while (mc.fetch(return_key, return_value, &return_key_length,
166 &return_value_length, &flags, &mc_rc))
167 {
168 assert(return_value.length() != 0);
169 }
170 assert(return_value_length == 0);
171 assert(mc_rc == MEMCACHED_END);
172
173 rc= mc.set_all(keys, keys, 50, 9);
174 assert(rc == true);
175
176 rc= mc.mget(keys);
177 assert(rc == true);
178
179 while ((mc.fetch(return_key, return_value, &return_key_length,
180 &return_value_length, &flags, &mc_rc)))
181 {
182 assert(return_value.length() != 0);
183 assert(mc_rc == MEMCACHED_SUCCESS);
184 assert(return_key_length == return_value_length);
185 assert(!memcmp(return_value.c_str(), return_key.c_str(), return_value_length));
186 }
187
188 return TEST_SUCCESS;
189}
96190
97test_st tests[] ={191test_st tests[] ={
98 {"basic", 0, basic_test },192 { "basic", 0, basic_test },
99 {"basic_master_key", 0, basic_master_key_test },193 { "basic_master_key", 0, basic_master_key_test },
194 { "increment_test", 0, increment_test },
195 { "mget", 1, mget_test },
196 { "mget_result_function", 1, mget_result_function },
100 {0, 0, 0}197 {0, 0, 0}
101};198};
102199

Subscribers

People subscribed via source and target branches