Giter Site home page Giter Site logo

cql's Introduction

CQL

Build status

Master:

Windows: Build status Linux: Build Status

Develop:

Windows: Build status Linux: Build Status

What is CQL?

CQL is a templated, header-only, database library for C++17 and above. It provides type safety, const correctness and automatic managment of your lookup tables, pretty much all using template "magic".

Cool. What can I store in it?

Pretty much anything as long as you tell CQL how to store it. CQL needs a couple of things to understand your type.

Okay, how do I use it?

The only thing you really need to do is to make your type decomposable:

struct User {
  User(std::string &&name, const int age) : id{ []() {
    static int lastID = -1; return ++lastID;
  }() }, name{ std::forward(name) }, age{ age } { }

  User(const int id, std::string &&name, const int age) : id(id),
    name{ std::forward(name) }, age{ age } { }

  int id;
  std::string name;
  int age;
};

If we want to take the above type and make it decomposable, we have to specify how many "parts" it can be decomposable to, what types these parts have and how to get them. The C++ has ways of specifying this already, so we're reusing them:

namespace std {
  // Getter
  template<size_t Ind>
  constexpr auto &get(User &u) noexcept {
    if constexpr(Ind == 0)
      return u.id;
    else if constexpr(Ind == 1)
      return u.name;
    else if constexpr(Ind == 2)
      return u.age;
  }

  // Const getter
  template<size_t Ind>
  constexpr auto &get(User const &u) noexcept {
    if constexpr(Ind == 0)
      return u.id;
    else if constexpr(Ind == 1)
      return u.name;
    else if constexpr(Ind == 2)
      return u.age;
  }
}

// How many parts this decomposes into
template <> struct std::tuple_size<User> : public std::integral_constant<size_t, 3> { };

// Type of each part, we peek at our get() for the answer.
template<size_t Ind> struct std::tuple_element<Ind, User> {
  using type = decltype(std::get<User, Ind>);
};

Cool. Your type is decomposable (bonus: this means this code can be used).

for(auto &[id, username, age] : users)
  std::cout << "[" << id << "]: " << username << " is " << age << " years old.\n";

That's it. Now you can store your type in CQL and do fast queries on any of these parts.

Lookups

First of all, all lookup tables use transparent comparators. Feel free to use lookup methods with anything comparable to your type parts.

To get your data out again, the simplest method is Entry const *Table::lookup<N>(T const &). This will do a lookup on the Nth part of your type and grab one that is equal and hand you a pointer to it.

Iteration

You can iterate over the container like any other:

CQL::Table<MyType> table;

table.emplace("Hello", "world!");

for(MyType const &val : table) {
  std::cout << val << "\n";
}

This iterates over the default lookup table for your objects. If one isn't defined, iteration order is the same as the address of the objects. If you want to iterate over a specific variable, you use vbegin<N>() and vend<N>() iterators:

CQL::Table<MyType> table;

table.emplace("Hello", "world!");

for(auto it = table.vbegin<0>(); it != table.vend<0>(); ++ it) {
  std::cout << *it << "\n";
}

There are also rvbegin<N>() and rvend<N>() for your reverse iteration needs.

Updating entries

Something that you might notice quite quickly is that when you're working with your objects inside the tables, you are in one way or another handed a MyType const &. This is to prevent accidental writing to the non-mutable members. Writing to these will not update the lookup tables, so please be const correct.

You can update values by asking the Table nicely. Just pass your Entry const * into Table::update(Entry const *, T &&newVal) with the value you want to set. There are also similar functions like Table::swap(Entry const *, T &&newVal) (swaps new value with the current one). You can also pass these an iterator and change any part that you're not currently iterating over.

Queries

Now you want to get down and dirty with some awesome queries without wasting any time looping through your entire table in linear time.

All query results are passed to a Functor using the >>= operator:

CQL::Table<MyType> table;

std::vector<MyType> tableDump;

table.all() >>= [](MyType const &entry) {
  tableDump.emplace_back(entry);
}

You can apply the range<N>(lower, upper) query to get all the entries where lower <= std::get<N>(entry) && std::get<N>(entry) <= upper. For example:

CQL::Table<MyType> table;

std::vector<MyType> smallAges;

table.range<2>(0, 10) >>= [](MyType const &entry) {
  smallAges.emplace_back(entry);
}

You probably don't want to store them into your container though, but you can work with your objects just like normal inside your functor.

Ranges can combine with set intersect or union:

table.range<0>(0, 0) || table.range<1>(0, 0) >>= [...];
table.range<0>(0, 0) && table.range<1>(0, 0) >>= [...];

And you can combine any valid range expression with a predicate:

(db.range<0>(0, 0) || db.range<1>(0, 0))
    && db.pred([](Point const &entry) {
  return entry.first != entry.second;
}) >>= [&](Point const &entry) {
  std::cout << "Point: " << entry << "\n";
};

The above code first does a union of the sets where a Point lies on the x and y axis respectively. Then the predicate throws out any Points where x == y.

Further customization

CQL becomes more powerful the more you tell it about your types. You can specialize a data structure to tell CQL that you want to enforce uniqueness over, for example, user IDs:

namespace CQL::Custom {
  template<>
  struct Unique<User, 0> {
    constexpr Uniqueness operator()() const {
      return Uniqueness::EnforceUnique;
    }
  };
}

Or setting the default iteration order and lookup. Setting this value lets CQL use something else than the address of the objects for lookups and completely eliminates this table and its related code. This should be used if you have a small data type, like an integer, that is also enforced or, alternatively, assumed unique (see uniqueness in the example above).

template<>
struct DefaultLookup<SimpleUser> {
  constexpr size_t operator()() const { return 0; }
};

You can always look in the Tests directory to see some sample implementations of anything in this readme.

cql's People

Contributors

n00byedge avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.