connected the first working version of memgraph
This commit is contained in:
parent
9f8761032f
commit
768ad5e451
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@
|
||||
*.swo
|
||||
*.out
|
||||
*.dSYM/
|
||||
memgraph
|
||||
|
4
Makefile
4
Makefile
@ -1,6 +1,6 @@
|
||||
CXX=clang++
|
||||
CFLAGS=-std=c++1y -Wall -O2
|
||||
LDFLAGS=-luv -lhttp_parser speedy/r3/.libs/libr3.a -L/usr/local/lib -lpcre
|
||||
CFLAGS=-std=c++1y -O2 -Wall -Wno-unknown-pragmas
|
||||
LDFLAGS=-luv -lhttp_parser speedy/r3/.libs/libr3.a -L/usr/local/lib -lpcre -pthread
|
||||
|
||||
INC=-I./
|
||||
SOURCES=$(wildcard *.cpp)
|
||||
|
@ -1,6 +1,7 @@
|
||||
# this script generates the include.hpp file for restful resources
|
||||
import re
|
||||
import os
|
||||
from itertools import chain
|
||||
|
||||
print "generating include.hpp file"
|
||||
|
||||
@ -16,27 +17,38 @@ if os.path.isfile(include_path):
|
||||
class Resource(object):
|
||||
""" represents a restful resource class for speedy """
|
||||
|
||||
def __init__(self, filename):
|
||||
def __init__(self, filename, class_name, url):
|
||||
self.filename = filename
|
||||
self.class_name = None
|
||||
self.class_name = class_name
|
||||
self.url = url
|
||||
|
||||
with open(os.path.join(resource_path, filename)) as f:
|
||||
class_name = re.compile('\s*class\s*(\w+)\s*\:')
|
||||
|
||||
for line in f:
|
||||
result = re.search(class_name, line)
|
||||
def scan_resources(filename):
|
||||
|
||||
if result is None:
|
||||
continue
|
||||
with open(os.path.join(resource_path, filename)) as f:
|
||||
url_regex = re.compile('#pragma\s+url\s+([^\s]+)\s+')
|
||||
class_name_regex = re.compile('\s*class\s*(\w+)\s*\:')
|
||||
|
||||
self.class_name = result.group(1)
|
||||
lines = f.readlines()
|
||||
pairs = zip(lines, lines[1:])
|
||||
|
||||
break
|
||||
for first, second in pairs:
|
||||
url = re.search(url_regex, first)
|
||||
|
||||
if url is None:
|
||||
continue
|
||||
|
||||
class_name = re.search(class_name_regex, second)
|
||||
|
||||
if class_name is None:
|
||||
continue
|
||||
|
||||
yield Resource(filename, class_name.group(1), url.group(1))
|
||||
|
||||
|
||||
def load_resources():
|
||||
resources = [Resource(f) for f in os.listdir(resource_path)
|
||||
if f.endswith('.hpp')]
|
||||
resources = chain(*[scan_resources(f) for f in os.listdir(resource_path)
|
||||
if f.endswith('.hpp')])
|
||||
|
||||
return [r for r in resources if r.class_name is not None]
|
||||
|
||||
@ -48,9 +60,10 @@ def write_includes(file, resources):
|
||||
|
||||
|
||||
def write_inits(file, resources):
|
||||
for class_name in [resource.class_name for resource in resources]:
|
||||
print 'writing init for', class_name
|
||||
file.write(' insert<{}>(app);\n'.format(class_name))
|
||||
for class_name, url in [(r.class_name, r.url) for r in resources]:
|
||||
print('writing init for {} -> {}'.format(class_name, url))
|
||||
file.write(' insert<{}>(container, "{}");\n'
|
||||
.format(class_name, url))
|
||||
|
||||
|
||||
def make_include_file():
|
||||
|
@ -1,24 +0,0 @@
|
||||
#ifndef MEMGRAPH_ANIMAL_HPP
|
||||
#define MEMGRAPH_ANIMAL_HPP
|
||||
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "api/restful/resource.hpp"
|
||||
|
||||
/* class Ani mal : public Resource<Animal, GET, POST> */
|
||||
/* { */
|
||||
/* public: */
|
||||
/* Animal(sp::Speedy& app) */
|
||||
/* : Resource(app, "/animal/{id:\\d+}/{name:\\w+}/commit") {} */
|
||||
|
||||
/* void get(sp::Request& req, sp::Response& res) */
|
||||
/* { */
|
||||
/* return res.send("Ok, here is a Dog"); */
|
||||
/* } */
|
||||
|
||||
/* void post(sp::Request& req, sp::Response& res) */
|
||||
/* { */
|
||||
/* return res.send("Oh, you gave me an animal?"); */
|
||||
/* } */
|
||||
/* }; */
|
||||
|
||||
#endif
|
@ -4,6 +4,8 @@
|
||||
* This file is autogenerated by the python script link_resources.py
|
||||
*
|
||||
* YOU SHOULD NOT EDIT THIS FILE MANUALLY!
|
||||
*
|
||||
* well, only if you're editing the template file, that's ok :)
|
||||
*/
|
||||
|
||||
#ifndef MEMGRAPH_API_RESOURCES_INCLUDE_HPP
|
||||
@ -12,22 +14,28 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "utils/ioc/container.hpp"
|
||||
#include "api/restful/resource.hpp"
|
||||
|
||||
#include "transactions/engine.hpp"
|
||||
#include "threading/task.hpp"
|
||||
#include "speedy/speedy.hpp"
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
static std::list<std::unique_ptr<RestfulResource>> resources;
|
||||
#include "node.hpp"
|
||||
|
||||
template <class T>
|
||||
void insert(sp::Speedy& app)
|
||||
void insert(ioc::Container& container, const std::string& path)
|
||||
{
|
||||
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
|
||||
auto app = container.resolve<sp::Speedy>();
|
||||
auto resource = container.singleton<T, Task, Db>();
|
||||
resource->link(*app, path);
|
||||
}
|
||||
|
||||
void init(sp::Speedy& app)
|
||||
void init(ioc::Container& container)
|
||||
{
|
||||
insert<Node>(app);
|
||||
insert<Nodes>(container, "/node");
|
||||
insert<Node>(container, "/node/{id:\\d+}");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -4,6 +4,8 @@
|
||||
* This file is autogenerated by the python script link_resources.py
|
||||
*
|
||||
* YOU SHOULD NOT EDIT THIS FILE MANUALLY!
|
||||
*
|
||||
* well, only if you're editing the template file, that's ok :)
|
||||
*/
|
||||
|
||||
#ifndef MEMGRAPH_API_RESOURCES_INCLUDE_HPP
|
||||
@ -12,21 +14,37 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "utils/ioc/container.hpp"
|
||||
#include "api/restful/resource.hpp"
|
||||
|
||||
#include "transactions/engine.hpp"
|
||||
#include "threading/task.hpp"
|
||||
#include "speedy/speedy.hpp"
|
||||
|
||||
// for each file in this folder a script will generate an include directive if
|
||||
// this file contains any resources
|
||||
// e.g.
|
||||
// #include "node.hpp"
|
||||
// #include "relationship.hpp"
|
||||
<INCLUDE>
|
||||
|
||||
static std::list<std::unique_ptr<RestfulResource>> resources;
|
||||
|
||||
template <class T>
|
||||
void insert(sp::Speedy& app)
|
||||
void insert(ioc::Container& container, const std::string& path)
|
||||
{
|
||||
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
|
||||
auto app = container.resolve<sp::Speedy>();
|
||||
auto resource = container.singleton<T, Task, Db>();
|
||||
resource->link(*app, path);
|
||||
}
|
||||
|
||||
void init(sp::Speedy& app)
|
||||
void init(ioc::Container& container)
|
||||
{
|
||||
// for each resource in a file included above, the script will generate a
|
||||
// linkage command call to the function above
|
||||
// e.g.
|
||||
// insert<CLASS>(PATH);
|
||||
//
|
||||
// insert<Nodes>("/node");
|
||||
// insert<Node>("/node/{id:\\d+}");
|
||||
<INIT>
|
||||
}
|
||||
|
||||
|
@ -1,31 +1,83 @@
|
||||
#ifndef MEMGRAPH_API_RESOURCES_NODE_HPP
|
||||
#define MEMGRAPH_API_RESOURCES_NODE_HPP
|
||||
|
||||
#include "api/restful/resource.hpp"
|
||||
#include <random>
|
||||
|
||||
class Node : public Resource<Node, GET, POST>
|
||||
#include "api/restful/resource.hpp"
|
||||
#include "debug/log.hpp"
|
||||
|
||||
#pragma url /node
|
||||
class Nodes : public Resource<Nodes, POST>
|
||||
{
|
||||
public:
|
||||
Node(sp::Speedy& app) : Resource(app, "/node") {}
|
||||
|
||||
void get(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
}
|
||||
using Resource::Resource;
|
||||
|
||||
void post(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
task->run([this, &req]() {
|
||||
// start a new transaction and obtain a reference to it
|
||||
auto t = db->tx_engine.begin();
|
||||
|
||||
// insert a new vertex in the graph
|
||||
auto atom = db->graph.vertices.insert(t);
|
||||
|
||||
// a new version was created and we got an atom along with the
|
||||
// first version. obtain a pointer to the first version
|
||||
//
|
||||
// nullptr
|
||||
// ^
|
||||
// |
|
||||
// [Vertex v1]
|
||||
// ^
|
||||
// |
|
||||
// [Atom id=k] k {1, 2, ...}
|
||||
//
|
||||
auto node = atom->first();
|
||||
|
||||
// TODO read the JSON body and store the properties in the
|
||||
// first version
|
||||
//
|
||||
// for(key, value in body)
|
||||
// node->properties[key] = value;
|
||||
|
||||
// commit the transaction
|
||||
db->tx_engine.commit(t);
|
||||
|
||||
// return the node we created so we can send it as a response body
|
||||
return node;
|
||||
},
|
||||
[&req, &res](Vertex* node) {
|
||||
// make a string buffer
|
||||
std::string buffer;
|
||||
|
||||
// dump properties in this buffer
|
||||
node->properties.dump(buffer);
|
||||
|
||||
// respond to the use with the buffer
|
||||
return res.send(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#pragma url /node/{id:\\d+}
|
||||
class Node : public Resource<Node, GET, PUT, DELETE>
|
||||
{
|
||||
public:
|
||||
using Resource::Resource;
|
||||
|
||||
void get(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send(req.url);
|
||||
}
|
||||
|
||||
void put(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
return res.send(req.url);
|
||||
}
|
||||
|
||||
void del(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
return res.send(req.url);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,157 +1,53 @@
|
||||
#ifndef MEMGRAPH_API_RESOURCE_HPP
|
||||
#define MEMGRAPH_API_RESOURCE_HPP
|
||||
#ifndef MEMGRAPH_API_RESTFUL_RESOURCE_HPP
|
||||
#define MEMGRAPH_API_RESTFUL_RESOURCE_HPP
|
||||
|
||||
#include <memory>
|
||||
#include "api/restful/restful_resource.hpp"
|
||||
|
||||
#include "database/db.hpp"
|
||||
#include "threading/task.hpp"
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
/** @brief GET handler method for the resource
|
||||
* Contains the code for registering GET handler for a URL to Speedy
|
||||
*/
|
||||
struct GET
|
||||
{
|
||||
/** @brief Links ::get handler to speedy for a given path
|
||||
*
|
||||
* @tparam T Class type containing the required handler
|
||||
* @param app Instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
* @param resource Object containing ::get http::request_cb_t handler
|
||||
*/
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.get(path, std::bind(&T::get, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief POST handler method for the resource
|
||||
* Contains the code for registering POST handler for a URL to Speedy
|
||||
*/
|
||||
struct POST
|
||||
{
|
||||
/** @brief Links ::post handler to speedy for a given path
|
||||
*
|
||||
* @tparam T Class type containing the required handler
|
||||
* @param app Instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
* @param resource Object containing ::post http::request_cb_t handler
|
||||
*/
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.post(path, std::bind(&T::post, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
struct PUT
|
||||
{
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.put(path, std::bind(&T::put, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
struct DELETE
|
||||
{
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.del(path, std::bind(&T::del, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/** @brief Registers a method for a path to speedy
|
||||
*
|
||||
* @tparam T Derived class containing the handler of the method
|
||||
* @tparam M A method to register
|
||||
*/
|
||||
template <class T, class M>
|
||||
struct Method : public M
|
||||
{
|
||||
/** Registers a route handler for the resource
|
||||
*
|
||||
* Registers a method handler for M on the given URL
|
||||
*
|
||||
* @param app instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
*/
|
||||
Method(sp::Speedy& app, const std::string& path)
|
||||
{
|
||||
M::link(app, path, static_cast<T&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief Generates the Method<T, M> inheritance for each M in Ms
|
||||
*
|
||||
* Implemented inheriting recursively using variadic templates
|
||||
*
|
||||
* @tparam T Derived class containing handlers for each method M in Ms
|
||||
* @tparam Ms... Methods to register
|
||||
*/
|
||||
template <class T, class... Ms>
|
||||
struct Methods;
|
||||
|
||||
/** @brief specialization of the struct Methods<T, Ms...>
|
||||
*
|
||||
* Unrolls one method M and generates a handler for this method by inheriting
|
||||
* from Method<T, M> and generates the rest of the handlers recursively by
|
||||
* inheriting from Methods<T, Ms...> and unrolling one M each time.
|
||||
*
|
||||
* @tparam T Derived class containing handlers for each method M in Ms
|
||||
* @tparam M Unrolled method M for which to generate a handler
|
||||
* @tparam Ms... The rest of the methods
|
||||
*/
|
||||
template <class T, class M, class... Ms>
|
||||
struct Methods<T, M, Ms...> : public Method<T, M>, public Methods<T, Ms...>
|
||||
{
|
||||
Methods(sp::Speedy& app, const std::string& path)
|
||||
: Method<T, M>(app, path), Methods<T, Ms...>(app, path) {}
|
||||
};
|
||||
|
||||
/** @brief specialization of the struct Methods<T, Ms...>
|
||||
*
|
||||
* Specializes the recursion termination case containing only one method M
|
||||
*
|
||||
* @tparam T Derived class containing handlers for method M
|
||||
* @tparam M Unrolled method M for which to generate a handler
|
||||
*/
|
||||
template <class T, class M>
|
||||
struct Methods<T, M> : public Method<T, M>
|
||||
{
|
||||
using Method<T, M>::Method;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct RestfulResource {};
|
||||
|
||||
/** @brief Represents a restful resource
|
||||
*
|
||||
* Automatically registers get, put, post, del... methods inside the derived
|
||||
* class T. Methods are given as a template parameter to the class. Valid
|
||||
* template parameters are classes which implement a function
|
||||
*
|
||||
* void link(sp::Speedy&, const std::string&, T& resource)
|
||||
*
|
||||
* which registers a method you want to use with speedy
|
||||
*
|
||||
* @tparam T Derived class (CRTP)
|
||||
* @tparam Ms... HTTP methods to register for this resource (GET, POST...)
|
||||
*/
|
||||
template <class T, class... Ms>
|
||||
class Resource : public detail::Methods<T, Ms...>, public RestfulResource
|
||||
class Resource
|
||||
{
|
||||
public:
|
||||
Resource(sp::Speedy& app, const std::string& path)
|
||||
: detail::Methods<T, Ms...>(app, path) {}
|
||||
/** @brief Resource constructor
|
||||
*
|
||||
* List ALL the dependencies here so that ioc can resolve them and store
|
||||
* them to protected members so that derived resources can access them
|
||||
*
|
||||
* @param task shared_ptr to the task dispatching library
|
||||
* @param db shared_ptr to the database instance
|
||||
*/
|
||||
Resource(Task::sptr task, Db::sptr db)
|
||||
: task(task), db(db) {}
|
||||
|
||||
/** @brief Link all resources to an instance of speedy
|
||||
*
|
||||
* link_resources.py generates an include.hpp file which includes and
|
||||
* instantinates all available resources. The include.hpp file also calls
|
||||
* this method to link the resources to speedy for a given path
|
||||
*/
|
||||
void link(sp::Speedy& app, const std::string& path)
|
||||
{
|
||||
// make sure this is called once even if someone actually calls this
|
||||
// function multiple times
|
||||
std::call_once(once_flag, [this, &app, &path]() {
|
||||
Restful<T, Ms...>(static_cast<T&>(*this), app, path);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
// all resources have pointers to these instances add everything else
|
||||
// neccessary here as a shared_ptr and also include it in the constructor
|
||||
// and modify the include.hpp.template to include the new dependencies for
|
||||
// resource linking
|
||||
Task::sptr task;
|
||||
Db::sptr db;
|
||||
|
||||
private:
|
||||
std::once_flag once_flag;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
156
api/restful/restful_resource.hpp
Normal file
156
api/restful/restful_resource.hpp
Normal file
@ -0,0 +1,156 @@
|
||||
#ifndef MEMGRAPH_API_RESTFUL_RESTFUL_RESOURCE_HPP
|
||||
#define MEMGRAPH_API_RESTFUL_RESTFUL_RESOURCE_HPP
|
||||
|
||||
#include <memory>
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
/** @brief GET handler method for the resource
|
||||
* Contains the code for registering GET handler for a URL to Speedy
|
||||
*/
|
||||
struct GET
|
||||
{
|
||||
/** @brief Links ::get handler to speedy for a given path
|
||||
*
|
||||
* @tparam T Class type containing the required handler
|
||||
* @param app Instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
* @param resource Object containing ::get http::request_cb_t handler
|
||||
*/
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.get(path, std::bind(&T::get, std::ref(resource), _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief POST handler method for the resource
|
||||
* Contains the code for registering POST handler for a URL to Speedy
|
||||
*/
|
||||
struct POST
|
||||
{
|
||||
/** @brief Links ::post handler to speedy for a given path
|
||||
*
|
||||
* @tparam T Class type containing the required handler
|
||||
* @param app Instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
* @param resource Object containing ::post http::request_cb_t handler
|
||||
*/
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.post(path, std::bind(&T::post, std::ref(resource), _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
struct PUT
|
||||
{
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.put(path, std::bind(&T::put, std::ref(resource), _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
struct DELETE
|
||||
{
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.del(path, std::bind(&T::del, std::ref(resource), _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/** @brief Registers a method for a path to speedy
|
||||
*
|
||||
* @tparam T Derived class containing the handler of the method
|
||||
* @tparam M A method to register
|
||||
*/
|
||||
template <class T, class M>
|
||||
struct Method : public M
|
||||
{
|
||||
/** Registers a route handler for the resource
|
||||
*
|
||||
* Registers a method handler for M on the given URL
|
||||
*
|
||||
* @param app instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
*/
|
||||
Method(T& resource, sp::Speedy& app, const std::string& path)
|
||||
{
|
||||
M::link(app, path, resource);
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief Generates the Method<T, M> inheritance for each M in Ms
|
||||
*
|
||||
* Implemented inheriting recursively using variadic templates
|
||||
*
|
||||
* @tparam T Derived class containing handlers for each method M in Ms
|
||||
* @tparam Ms... Methods to register
|
||||
*/
|
||||
template <class T, class... Ms>
|
||||
struct Methods;
|
||||
|
||||
/** @brief specialization of the struct Methods<T, Ms...>
|
||||
*
|
||||
* Unrolls one method M and generates a handler for this method by inheriting
|
||||
* from Method<T, M> and generates the rest of the handlers recursively by
|
||||
* inheriting from Methods<T, Ms...> and unrolling one M each time.
|
||||
*
|
||||
* @tparam T Derived class containing handlers for each method M in Ms
|
||||
* @tparam M Unrolled method M for which to generate a handler
|
||||
* @tparam Ms... The rest of the methods
|
||||
*/
|
||||
template <class T, class M, class... Ms>
|
||||
struct Methods<T, M, Ms...> : public Method<T, M>, public Methods<T, Ms...>
|
||||
{
|
||||
Methods(T& resource, sp::Speedy& app, const std::string& path)
|
||||
: Method<T, M>(resource, app, path),
|
||||
Methods<T, Ms...>(resource, app, path) {}
|
||||
};
|
||||
|
||||
/** @brief specialization of the struct Methods<T, Ms...>
|
||||
*
|
||||
* Specializes the recursion termination case containing only one method M
|
||||
*
|
||||
* @tparam T Derived class containing handlers for method M
|
||||
* @tparam M Unrolled method M for which to generate a handler
|
||||
*/
|
||||
template <class T, class M>
|
||||
struct Methods<T, M> : public Method<T, M>
|
||||
{
|
||||
using Method<T, M>::Method;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/** @brief Represents a restful resource
|
||||
*
|
||||
* Automatically registers get, put, post, del... methods inside the derived
|
||||
* class T. Methods are given as a template parameter to the class. Valid
|
||||
* template parameters are classes which implement a function
|
||||
*
|
||||
* void link(sp::Speedy&, const std::string&, T& resource)
|
||||
*
|
||||
* which registers a method you want to use with speedy
|
||||
*
|
||||
* @tparam T Derived class (CRTP)
|
||||
* @tparam Ms... HTTP methods to register for this resource (GET, POST...)
|
||||
*/
|
||||
template <class T, class... Ms>
|
||||
class Restful : public detail::Methods<T, Ms...>
|
||||
{
|
||||
public:
|
||||
Restful(T& resource, sp::Speedy& app, const std::string& path)
|
||||
: detail::Methods<T, Ms...>(resource, app, path) {}
|
||||
};
|
||||
|
||||
#endif
|
@ -1,20 +1,17 @@
|
||||
#ifndef MEMGRAPH_DATABASE_DB_HPP
|
||||
#define MEMGRAPH_DATABASE_DB_HPP
|
||||
|
||||
#include "transactions/transaction_engine.hpp"
|
||||
#include "storage/graph.hpp"
|
||||
#include "transactions/engine.hpp"
|
||||
#include "transactions/commit_log.hpp"
|
||||
|
||||
class Db
|
||||
{
|
||||
public:
|
||||
static Db& get()
|
||||
{
|
||||
static Db db;
|
||||
return db;
|
||||
}
|
||||
using sptr = std::shared_ptr<Db>;
|
||||
|
||||
tx::CommitLog commit_log;
|
||||
tx::TransactionEngine transaction_engine;
|
||||
Graph graph;
|
||||
tx::Engine tx_engine;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
128
io/uv/blockbuffer.hpp
Normal file
128
io/uv/blockbuffer.hpp
Normal file
@ -0,0 +1,128 @@
|
||||
#ifndef MEMGRAPH_IO_UV_BLOCKBUFFER_HPP
|
||||
#define MEMGRAPH_IO_UV_BLOCKBUFFER_HPP
|
||||
|
||||
#include <cstring>
|
||||
#include <uv.h>
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
template <size_t block_size>
|
||||
class BlockBuffer
|
||||
{
|
||||
static BlockAllocator<block_size> allocator;
|
||||
|
||||
struct Block : public uv_buf_t
|
||||
{
|
||||
Block()
|
||||
{
|
||||
// acquire a new block of memory for this buffer
|
||||
base = static_cast<char*>(allocator.acquire());
|
||||
len = 0;
|
||||
}
|
||||
|
||||
~Block()
|
||||
{
|
||||
// release the block of memory previously acquired
|
||||
allocator.release(base);
|
||||
}
|
||||
|
||||
size_t append(const char* data, size_t size)
|
||||
{
|
||||
// compute the remaining capacity for this block
|
||||
auto capacity = block_size - len;
|
||||
|
||||
// if the capacity is smaller than the requested size, copy only
|
||||
// up to the remaining capacity
|
||||
if(size > capacity)
|
||||
size = capacity;
|
||||
|
||||
std::memcpy(base + len, data, size);
|
||||
len += size;
|
||||
|
||||
// return how much we've copied
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
BlockBuffer()
|
||||
{
|
||||
// create the first buffer
|
||||
buffers.emplace_back();
|
||||
}
|
||||
|
||||
~BlockBuffer()
|
||||
{
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
BlockBuffer(BlockBuffer&) = delete;
|
||||
BlockBuffer(BlockBuffer&&) = delete;
|
||||
|
||||
size_t count() const
|
||||
{
|
||||
return buffers.size();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
// pop all buffers except for the first one since we need to allocate
|
||||
// it again anyway so why not keep it in the first place
|
||||
while(count() > 1)
|
||||
buffers.pop_back();
|
||||
|
||||
// pretend we just allocated our first buffer and set it's length to 0
|
||||
buffers.back().len = 0;
|
||||
}
|
||||
|
||||
BlockBuffer& operator<<(const std::string& data)
|
||||
{
|
||||
append(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void append(const std::string& data)
|
||||
{
|
||||
append(data.c_str(), data.size());
|
||||
}
|
||||
|
||||
void append(const char* data, size_t size)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
// try to copy as much data as possible
|
||||
auto copied = buffers.back().append(data, size);
|
||||
|
||||
// if we managed to copy everything, we're done
|
||||
if(copied == size)
|
||||
break;
|
||||
|
||||
// move the pointer past the copied part
|
||||
data += copied;
|
||||
|
||||
// reduce the total size by the number of copied items
|
||||
size -= copied;
|
||||
|
||||
// since we ran out of space, construct a new buffer
|
||||
buffers.emplace_back();
|
||||
}
|
||||
}
|
||||
|
||||
operator uv_buf_t*()
|
||||
{
|
||||
return buffers.data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Block> buffers;
|
||||
};
|
||||
|
||||
template <size_t block_size>
|
||||
BlockAllocator<block_size> BlockBuffer<block_size>::allocator;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,6 +1,7 @@
|
||||
#include "tcpstream.hpp"
|
||||
#include "uvbuffer.hpp"
|
||||
#include "uvloop.hpp"
|
||||
#include "blockbuffer.hpp"
|
||||
|
||||
#include "tcpstream.inl"
|
||||
#include "uvbuffer.inl"
|
||||
|
@ -12,6 +12,8 @@ namespace uv
|
||||
class UvLoop final
|
||||
{
|
||||
public:
|
||||
using sptr = std::shared_ptr<UvLoop>;
|
||||
|
||||
enum Mode {
|
||||
Default = UV_RUN_DEFAULT,
|
||||
Once = UV_RUN_ONCE,
|
||||
|
29
memgraph.cpp
29
memgraph.cpp
@ -1,22 +1,35 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "debug/log.hpp"
|
||||
#include "utils/ioc/container.hpp"
|
||||
|
||||
#include "database/db.hpp"
|
||||
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "api/resources/include.hpp"
|
||||
|
||||
#include "utils/auto_scope.hpp"
|
||||
#include "threading/pool.hpp"
|
||||
#include "threading/task.hpp"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
int main()
|
||||
{
|
||||
uv::UvLoop loop;
|
||||
sp::Speedy app(loop);
|
||||
ioc::Container container;
|
||||
|
||||
init(app);
|
||||
container.singleton<Db>();
|
||||
|
||||
http::Ipv4 ip("0.0.0.0", 3400);
|
||||
app.listen(ip);
|
||||
auto loop = container.singleton<uv::UvLoop>();
|
||||
auto app = container.singleton<sp::Speedy, uv::UvLoop>("/db/data");
|
||||
|
||||
loop.run(uv::UvLoop::Mode::Default);
|
||||
container.singleton<Pool>(4);
|
||||
container.singleton<Task, uv::UvLoop, Pool>();
|
||||
|
||||
init(container);
|
||||
|
||||
http::Ipv4 ip("0.0.0.0", 7474);
|
||||
app->listen(ip);
|
||||
|
||||
loop->run(uv::UvLoop::Mode::Default);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -7,8 +7,6 @@
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
|
@ -66,7 +66,6 @@ void HttpServer<Req, Res>::
|
||||
conn.close();
|
||||
|
||||
buffer_allocator.release(buf->base);
|
||||
//delete buf->base;
|
||||
}
|
||||
|
||||
template <class Req, class Res>
|
||||
|
@ -9,6 +9,8 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
static constexpr size_t buffer_size = 65536;
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpConnection;
|
||||
|
||||
@ -30,7 +32,7 @@ public:
|
||||
|
||||
private:
|
||||
connection_t& connection;
|
||||
uv::UvBuffer buffer;
|
||||
uv::BlockBuffer<buffer_size> buffer;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,14 +9,11 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
constexpr size_t buffer_size = 65536;
|
||||
|
||||
static BlockAllocator<sizeof(uv_write_t)> write_req_allocator;
|
||||
|
||||
template <class Req, class Res>
|
||||
Response<Req, Res>::Response(connection_t& connection)
|
||||
: status(Status::Ok), connection(connection),
|
||||
buffer(buffer_size) {}
|
||||
: status(Status::Ok), connection(connection), buffer() {}
|
||||
|
||||
template <class Req, class Res>
|
||||
void Response<Req, Res>::send(Status status, const std::string& body)
|
||||
@ -45,7 +42,7 @@ void Response<Req, Res>::send(const std::string& body)
|
||||
|
||||
buffer << "\r\n" << body;
|
||||
|
||||
uv_write(write_req, connection.client, buffer, 1,
|
||||
uv_write(write_req, connection.client, buffer, buffer.count(),
|
||||
[](uv_write_t* write_req, int) {
|
||||
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(write_req->data);
|
||||
|
@ -47,17 +47,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class MatchEntry
|
||||
public:
|
||||
class Route
|
||||
{
|
||||
public:
|
||||
MatchEntry(Method method, const std::string& path)
|
||||
Route(r3::node* node, Method method, const std::string& path)
|
||||
{
|
||||
entry = r3::match_entry_createl(path.c_str(), path.size());
|
||||
entry->request_method = method;
|
||||
route = r3::r3_tree_match_route(node, entry);
|
||||
}
|
||||
|
||||
~MatchEntry()
|
||||
~Route()
|
||||
{
|
||||
r3::match_entry_free(entry);
|
||||
}
|
||||
@ -67,22 +68,17 @@ private:
|
||||
return entry;
|
||||
}
|
||||
|
||||
private:
|
||||
r3::match_entry* entry;
|
||||
r3::route* route;
|
||||
};
|
||||
|
||||
public:
|
||||
class Route
|
||||
{
|
||||
public:
|
||||
Route(r3::route* route) : route(route) {}
|
||||
|
||||
bool exists() const
|
||||
{
|
||||
return route != nullptr;
|
||||
}
|
||||
|
||||
void populate(sp::Request& req)
|
||||
{
|
||||
for(int i = 0; i < entry->vars->len; ++i)
|
||||
req.params.emplace_back(entry->vars->tokens[i]);
|
||||
}
|
||||
|
||||
void operator()(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
assert(route != nullptr);
|
||||
@ -92,6 +88,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
r3::match_entry* entry;
|
||||
r3::route* route;
|
||||
};
|
||||
|
||||
@ -124,24 +121,7 @@ public:
|
||||
|
||||
Route match(Method method, const std::string& path)
|
||||
{
|
||||
auto entry_m = MatchEntry(method, path);
|
||||
return Route(r3::r3_tree_match_route(root, entry_m));
|
||||
|
||||
/* if(!route.exists()) */
|
||||
/* return route; */
|
||||
|
||||
/* r3::match_entry* entry = entry_m; */
|
||||
|
||||
/* std::cout << "Eetry Matched!" << std::endl */
|
||||
/* << "path = " << std::string(entry->path, entry->path_len) << std::endl */
|
||||
/* << "host = " << std::string(entry->host, entry->host_len) << std::endl */
|
||||
/* << "remote_addr = " << std::string(entry->remote_addr, entry->remote_addr_len) << std::endl */
|
||||
/* << "tokens_len = " << entry->vars->len << std::endl; */
|
||||
|
||||
/* for(int i = 0; i < entry->vars->len; ++i) */
|
||||
/* std::cout << "token " << i << " = " << std::string(entry->vars->tokens[i]) << std::endl; */
|
||||
|
||||
//return route;
|
||||
return Route(root, method, path);
|
||||
}
|
||||
|
||||
void compile()
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef MEMGRAPH_SPEEDY_REQUEST_HPP
|
||||
#define MEMGRAPH_SPEEDY_REQUEST_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "http/request.hpp"
|
||||
|
||||
namespace sp
|
||||
@ -10,6 +12,8 @@ class Request : public http::Request
|
||||
{
|
||||
public:
|
||||
using http::Request::Request;
|
||||
|
||||
std::vector<std::string> params;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -23,33 +23,36 @@ namespace sp
|
||||
class Speedy
|
||||
{
|
||||
public:
|
||||
using sptr = std::shared_ptr<Speedy>;
|
||||
|
||||
using server_t = http::HttpServer<Request, Response>;
|
||||
using request_cb_t = server_t::request_cb_t;
|
||||
|
||||
Speedy(uv::UvLoop& loop, size_t capacity = 100)
|
||||
: server(loop), router(capacity) {}
|
||||
Speedy(uv::UvLoop::sptr loop, const std::string& prefix = "",
|
||||
size_t capacity = 100)
|
||||
: server(*loop), prefix(std::move(prefix)), router(capacity) {}
|
||||
|
||||
Speedy(Speedy&) = delete;
|
||||
Speedy(Speedy&&) = delete;
|
||||
|
||||
void get(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::GET, path, cb);
|
||||
router.insert(R3::Method::GET, join(prefix, path), cb);
|
||||
}
|
||||
|
||||
void post(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::POST, path, cb);
|
||||
router.insert(R3::Method::POST, join(prefix, path), cb);
|
||||
}
|
||||
|
||||
void put(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::PUT, path, cb);
|
||||
router.insert(R3::Method::PUT, join(prefix, path), cb);
|
||||
}
|
||||
|
||||
void del(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::DELETE, path, cb);
|
||||
router.insert(R3::Method::DELETE, join(prefix, path), cb);
|
||||
}
|
||||
|
||||
void listen(const http::Ipv4& ip)
|
||||
@ -57,20 +60,43 @@ public:
|
||||
router.compile();
|
||||
|
||||
server.listen(ip, [this](Request& req, Response& res) {
|
||||
return res.send("Hello World");
|
||||
|
||||
auto route = router.match(R3::to_r3_method(req.method), req.url);
|
||||
|
||||
if(!route.exists())
|
||||
return res.send(http::Status::NotFound, "Resource not found");
|
||||
|
||||
route.populate(req);
|
||||
route(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
server_t server;
|
||||
std::string prefix;
|
||||
R3 router;
|
||||
|
||||
std::string join(const std::string& prefix, const std::string& path)
|
||||
{
|
||||
// check if prefix has a trailing /
|
||||
if(prefix.back() == '/')
|
||||
{
|
||||
// prefix has a / on the end so remove the / from the path
|
||||
// if it has it too. e.g /db/data/ + /node = /db/data/node
|
||||
if(path.front() == '/')
|
||||
return prefix + path.substr(1);
|
||||
|
||||
// there is only one / so it's ok to concat them
|
||||
return prefix + path;
|
||||
}
|
||||
|
||||
// the prefix doesn't have a slash
|
||||
if(path.front() == '/')
|
||||
// the path has though, so it's ok to concat them
|
||||
return prefix + path;
|
||||
|
||||
// neither the prefix or the path have a / so we need to add it
|
||||
return prefix + '/' + path;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -10,15 +10,17 @@ namespace model
|
||||
class Property
|
||||
{
|
||||
public:
|
||||
// shared_ptr is being used because of MVCC - when you clone a record, you
|
||||
// clone it's properties. when a single property is updated, a lot of
|
||||
// memory is being wasted. this way it is shared until you need to change
|
||||
// something and shared ptr ensures it's being properly tracked and
|
||||
// cleaned up after no one is using it
|
||||
|
||||
using sptr = std::shared_ptr<Property>;
|
||||
|
||||
virtual ~Property() {}
|
||||
template <class T, class... Args>
|
||||
static Property::sptr make(Args&&... args)
|
||||
{
|
||||
return std::shared_ptr<Property>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
Property() = default;
|
||||
virtual ~Property() = default;
|
||||
|
||||
virtual void dump(std::string& buffer) = 0;
|
||||
|
||||
template <class T>
|
||||
|
@ -13,6 +13,8 @@ class Pool : Lockable<std::mutex>
|
||||
{
|
||||
using task_t = std::function<void()>;
|
||||
public:
|
||||
using sptr = std::shared_ptr<Pool>;
|
||||
|
||||
Pool(size_t n = std::thread::hardware_concurrency()) : alive(true)
|
||||
{
|
||||
threads.reserve(n);
|
||||
@ -33,15 +35,11 @@ public:
|
||||
thread.join();
|
||||
}
|
||||
|
||||
template <class F, class... Args>
|
||||
void run(F&& f, Args&&... args)
|
||||
void run(task_t f)
|
||||
{
|
||||
{
|
||||
auto lock = acquire_unique();
|
||||
|
||||
tasks.emplace([&f, &args...]() {
|
||||
f(std::forward<Args>(args)...);
|
||||
});
|
||||
tasks.push(f);
|
||||
}
|
||||
|
||||
cond.notify_one();
|
||||
|
83
threading/task.hpp
Normal file
83
threading/task.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef MEMGRAPH_THREADING_TASK_HPP
|
||||
#define MEMGRAPH_THREADING_TASK_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "pool.hpp"
|
||||
#include "io/uv/uvloop.hpp"
|
||||
#include "utils/placeholder.hpp"
|
||||
|
||||
class Task
|
||||
{
|
||||
template <class T>
|
||||
using work_t = std::function<T(void)>;
|
||||
|
||||
template <class T>
|
||||
using after_work_t = std::function<void(T)>;
|
||||
|
||||
template <class T>
|
||||
struct Work
|
||||
{
|
||||
|
||||
Work(uv::UvLoop& loop, work_t<T> work, after_work_t<T> after_work)
|
||||
: work(std::move(work)), after_work(std::move(after_work))
|
||||
{
|
||||
uv_async_init(loop, &this->async, async_cb);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
result.set(std::move(work()));
|
||||
|
||||
async.data = static_cast<void*>(this);
|
||||
uv_async_send(&this->async);
|
||||
}
|
||||
|
||||
work_t<T> work;
|
||||
after_work_t<T> after_work;
|
||||
|
||||
Placeholder<T> result;
|
||||
|
||||
uv_async_t async;
|
||||
|
||||
private:
|
||||
static void async_cb(uv_async_t* handle)
|
||||
{
|
||||
auto work = static_cast<Work<T>*>(handle->data);
|
||||
|
||||
work->after_work(std::move(work->result.get()));
|
||||
|
||||
auto async_as_handle = reinterpret_cast<uv_handle_t*>(handle);
|
||||
|
||||
uv_close(async_as_handle, [](uv_handle_t* handle) {
|
||||
auto work = static_cast<Work<T>*>(handle->data);
|
||||
delete work;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
using sptr = std::shared_ptr<Task>;
|
||||
|
||||
Task(uv::UvLoop::sptr loop, Pool::sptr pool) : loop(loop), pool(pool) {}
|
||||
|
||||
Task(Task&) = delete;
|
||||
Task(Task&&) = delete;
|
||||
|
||||
template <class F1, class F2>
|
||||
void run(F1&& work, F2&& after_work)
|
||||
{
|
||||
using T = decltype(work());
|
||||
|
||||
auto w = new Work<T>(*loop, std::forward<F1>(work),
|
||||
std::forward<F2>(after_work));
|
||||
|
||||
pool->run([w]() { w->operator()(); });
|
||||
}
|
||||
|
||||
private:
|
||||
uv::UvLoop::sptr loop;
|
||||
Pool::sptr pool;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
#ifndef MEMGRAPH_MVCC_TRANSACTIONENGINE_HPP
|
||||
#define MEMGRAPH_MVCC_TRANSACTIONENGINE_HPP
|
||||
#ifndef MEMGRAPH_TRANSACTIONS_ENGINE_HPP
|
||||
#define MEMGRAPH_TRANSACTIONS_ENGINE_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
@ -22,10 +22,12 @@ public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class TransactionEngine : Lockable<SpinLock>
|
||||
class Engine : Lockable<SpinLock>
|
||||
{
|
||||
public:
|
||||
TransactionEngine() : counter(0) {}
|
||||
using sptr = std::shared_ptr<Engine>;
|
||||
|
||||
Engine() : counter(0) {}
|
||||
|
||||
const Transaction& begin()
|
||||
{
|
@ -69,23 +69,17 @@ public:
|
||||
return item->get();
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
std::shared_ptr<T> singleton()
|
||||
template <class T, class... Deps, class... Args>
|
||||
std::shared_ptr<T> singleton(Args&&... args)
|
||||
{
|
||||
auto item = std::make_shared<T>(resolve<Args>()...);
|
||||
auto item = std::make_shared<T>(resolve<Deps>()..., args...);
|
||||
items.emplace(key<T>(), Holdable::uptr(new Instance<T>(item)));
|
||||
return item;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void singleton(std::shared_ptr<T>&& item)
|
||||
{
|
||||
items.emplace(key<T>(), Holdable::uptr(new Instance<T>(item)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void factory(typename Creator<T>::func&& f)
|
||||
{
|
||||
{
|
||||
items[key<T>()] = std::move(Holdable::uptr(
|
||||
new Creator<T>(std::forward<typename Creator<T>::func>(f))
|
||||
));
|
||||
|
45
utils/placeholder.hpp
Normal file
45
utils/placeholder.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef MEMGRAPH_UTILS_PLACEHOLDER_HPP
|
||||
#define MEMGRAPH_UTILS_PLACEHOLDER_HPP
|
||||
|
||||
#include <utility>
|
||||
#include <ext/aligned_buffer.h>
|
||||
|
||||
template <class T>
|
||||
class Placeholder
|
||||
{
|
||||
public:
|
||||
Placeholder() = default;
|
||||
|
||||
Placeholder(Placeholder&) = delete;
|
||||
Placeholder(Placeholder&&) = delete;
|
||||
|
||||
~Placeholder()
|
||||
{
|
||||
if(initialized)
|
||||
get().~T();
|
||||
};
|
||||
|
||||
T& get() noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
void set(const T& item)
|
||||
{
|
||||
new (data._M_addr()) T(item);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void set(T&& item)
|
||||
{
|
||||
new (data._M_addr()) T(std::move(item));
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private:
|
||||
__gnu_cxx::__aligned_buffer<T> data;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user