Skip to content
This repository has been archived by the owner on May 27, 2024. It is now read-only.
/ lel Public archive

πŸ”€ Lambda Expression Library

License

Notifications You must be signed in to change notification settings

dawikur/lel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

LeL

Lambda Expression Library

Single header (after fusing) library with no external dependencies.

auto is_right_triangle = _a * _a + _b *_b == _c * _c;

is_right_triangle(3, 4, 5);

Language License

Master MasterLinuxBuild MasterWindowsBuild MasterCoverage


About

Lambda functions in c++ are cool, very cool. But they give quite some overhead in syntax requirements, which obscures very little functions. Consider:

auto is_even = [](auto i) { return i % 2 == 0; };

It is very short, nice lambda, but the actual condition is only around half of its body. Now lets see how it would look in lambda expression:

auto is_even = _i % 2 == 0;

Shorted, right? And, for me, much cleaner.

Examples

  using namespace lel;

simple

  auto plus_one = _x + 1;
  auto multiply = _x * _y;
  auto is_less  = _1 < _2;

  assert(plus_one(2) == 3);
  assert(multiply(2, 3) == 6);
  assert(is_less(2, 5));

more arguments

  auto compute_something = 1 + _x * 3 - _y * _y + _x * _z;

  assert(3 == compute_something(1, 2, 3));

own placeholders

  auto first_arg  = lel::placeholder<'1'>();
  auto second_arg = lel::placeholder<'2'>();

  auto not_equal = first_arg != second_arg;

  assert(not_equal(2, 3));

more complex

  auto add_of_vectors = *((*_x)[_1]) + **(_y[_2]);

  auto x_vec = std::make_unique<std::vector<std::unique_ptr<int>>>();
  x_vec->push_back(std::make_unique<int>(1));
  x_vec->push_back(std::make_unique<int>(2));
  x_vec->push_back(std::make_unique<int>(3));
  x_vec->push_back(std::make_unique<int>(4));

  auto y_vec = std::vector<std::unique_ptr<std::unique_ptr<int>>>();
  y_vec.push_back(std::make_unique<std::unique_ptr<int>>(std::make_unique<int>(6)));
  y_vec.push_back(std::make_unique<std::unique_ptr<int>>(std::make_unique<int>(7)));
  y_vec.push_back(std::make_unique<std::unique_ptr<int>>(std::make_unique<int>(8)));
  y_vec.push_back(std::make_unique<std::unique_ptr<int>>(std::make_unique<int>(9)));

  assert((4+7) == (add_of_vectors(3, 1, x_vec, y_vec)));

references

  int x = 5;

  auto add_to_x = _(x) += _y;

  add_to_x(8);

  assert(x == 13);

currying

  auto sum = _x + _y + _z;

  assert(6 == sum(1, 2, 3));
  assert(6 == sum(1)(2, 3));
  assert(6 == sum(1, 2)(3));
  assert(6 == sum(1)(2)(3));

function call

  auto duplicate = [](int i) {
    return i * 2;
  };

  auto call_with = _x._(_y);
  assert(8 == call_with(duplicate, 4));

  auto do_with_three = _x._(3);
  assert(6 == do_with_three(duplicate));

  auto times_two = _(duplicate)._(_y);
  assert(10 == times_two(5));

  auto agregate = [](int a, int b, int c, int d) {
    return a + b + c + d;
  };

  auto reduce = _(agregate)._(_a, _b, _c, _d);
  assert(20 == reduce(2, 4, 6, 8));

objects

  struct Object {
    Object(int value) : value(value) {}

    int Method(int arg) { return value * arg; }

    int value;
  };

  Object object(5);

field

  auto get_value = (&_x->*_y);
  auto set_value = get_value = _z;

  set_value(object, &Object::value, 8);
  assert(get_value(object, &Object::value) == 8);

method call

  auto call_method = get_value._(_z);

  assert(call_method(object, &Object::Method, 3) == 24);

Usage

Files There are two ways of including LeL:

  • in multiple headers, as it is in repository; everything is in include directory

  • in one fused-header;

    script/fuse will fuse all into build/lel.hpp

API The only things that should be used are:

  • namespace lel - with predefined placeholders
    • _ - instance of lel::reference class
    • _1 ... _9 / instances of lel::placeholder class
    • _A ... _Z |
    • _a ... _z /
    • placeholder - template alias for placeholders creation
    • reference - class for wrapping objects in a reference
#include "lel.hpp"

auto use_with_namespace = lel::_x + 2;

auto create_your_own    = lel::placeholder<'a'>();

/* or */ using namespace lel;
auto like_that          = 1 * _2;

Implementation Templates, so many templates. Cool stuff!

The separate types of placeholders are distinguist by template variable of type (by default, can be changed) char. So Placeholder<'a'> and Placeholder<'A'> are different types.

You can use any combination of them (only numbers, numbers and letters, etc) in any order or degree. The important thing is the arguments during evaluation will be assigned with alphabetic (ASCII alphabetic) order.

So:

auto expr = _1 - _x;

assert(-1 == expr(2, 3));
assert( 1 == expr(3, 2));

Tests

There is a bunch of tests but they are not needed for normal usage. You can run all of them with cmake:

mkdir build
cd build
cmake .. -DENABLE_TESTING=ON
cmake --build .
ctest -VV

Benchmarks

TBD

BLL - Boost Lambda Library

Yes, the same concept but this one uses a little bit newer C++. However the idea for this library came out independently.