Home > Blockchain >  Adding syntactic sugar to C
Adding syntactic sugar to C

Time:02-08

Edit 2:

New edit: it looks like C 20 has a new ranges library, which does what I want from the functional point of view. How would something similar be done on C 17 or earlier? Also, would the Kotlin syntactic sugar be possible? Mainly the person example:

val adam = Person("Adam").apply { 
    age = 20 // same as this.age = 20 or adam.age = 20
    city = "London"
}

Edit 1:

I don't know if my question was that clear, so I'll give an example using Rust's map.

This is how a map is done in rust:

let newVector = myVector.iter().map(|x|, x * 2)

This is how it is done in C

std::string s("hello");
std::vector<std::size_t> ordinals;
std::transform(s.begin(), s.end(), std::back_inserter(ordinals),
     [](unsigned char c) -> std::size_t { return c; });

This is a lot more verbose.

I'd like to add some syntactic sugar to do something like this instead:

std::string s("hello");
auto ordinals = my_ns::map(s,[](unsigned char c) -> std::size_t { return c; });

The implementation of my_ns::map could be something like this (I'm sure this will not work, it's just to show how it could be done)

template<typename T, U>
map(T t, std::function<U> f)
{
    std::vector<U> ordinals;
    std::transform(t.begin(), t.end(), std::back_inserter(ordinals),
     [](U c) -> f(c));
    return ordinals;
}

In this case ordinals doesn't need to be of type std::vector<std::size_t>, it could be of a type map which has a conversion for std::vector<T>. The reason for a type map is to be able to chain functions like reduce with map.

Original Question

Note: this is for a personal project only, I do not intend to use it in production or when working with C most of the time.

I have been using a lot of Kotlin lately and I'm dabbling a little with Rust and I love their higher level features. That got me wondering, can I create some syntactic sugar to emulate some of these features?

Some of the stuff I'd be trying to emulate(just an example)

Rust map:

let newVector = myVector.iter().map(|x|, x * 2)

Kotlin let

val adam = Person("Adam").apply { 
    age = 20 // same as this.age = 20 or adam.age = 20
    city = "London"
}

val str = "Hello"
str.let {
   println("The string's length is ${it.length}")
}

I do not plan on using the exact same syntax, but I'd like to know if it would be possible to do something like:

int[] arr = {1, 2, 3};
auto d_arr = map(arr, [](int x){return x*2}) // maybe return a map type to be able to use .reduce, etc

The first kotlin let example I wouldn't know how to do, but the second could be solved using lambdas as well.

I'm thinking of using new types and create conversions to std::vector and arrays, kind of how C#'s LINQ works.

Any tips on how to do this are appreciated.

Ps: I know about std::transform and other functions(I'd probably use them when implementing this), but I'd like a simpler API than the ones offered (and I also think that it would be a nice personal project).

CodePudding user response:

Your example can be changed to working C code fairly easily:

template <typename T, typename Fun> auto map(T t, Fun f) {
  std::vector<typename T::value_type> ordinals;
  std::transform(t.begin(), t.end(), std::back_inserter(ordinals), f);
  return ordinals;
}

This is neither very idiomatic nor optimized for performance, but it works. (Pre-resize the vector instead of using std::back_inserter and pass t by reference for a big performance boost.)

Edit: You can add reduce as follows:

template <typename T, typename R, typename Fun> auto reduce(T t, R init, Fun f) {
  return std::transform(t.begin(), t.end(), init, f);
}

And then combine them: reduce(map(x, [](auto a) {...}), 0, [](int& a, auto b) {...})

The obvious caveat: This is going to be really slow. These functions are fun to learn template concepts, but in practice, ranges or old-style std::reduce/... will be much, much faster.

CodePudding user response:

I'd like to know if it would be possible to do something like:

int[] arr = {1, 2, 3};
auto d_arr = map(arr, [](int x){return x*2})

"map" in this sense is called "transform" in C standard library. A working example:

auto double_ = [](auto x){ return x*2; }; // you may also use a normal function
auto doubled_view = std::views::all(arr)
                  | std::views::transform(double_);

to be able to chain functions like reduce with map.

You can reduce ranges. Unfortunately, the C 20 standard ranges don't come with a reduce function, so one has to use either another implementation of ranges, or use the iterator based functions instead. Example of latter:

auto sum = std::reduce(
     std::execution::unseq, // optional execution policy
     doubled_view.begin(),
     doubled_view.end(),
     0
     // you can use a functor to do something other than add
     );

conversions to std::vector

You can also create containers from ranges:

std::vector doubled_vector(
    doubled_view.begin(), doubled_view.end());

Note that creating containers for intermediate stages of range views can have unnecessary overhead.

  •  Tags:  
  • Related