Tech Off Thread

10 posts

New C++ runtime library

Back to Forum: Tech Off
  • User profile image
    sysrpl

    I am working on a library for C++ which is small, has no dependencies other than itself (i'm not using boost), works on all platforms, and most importantly is as friendly as C#.

    This morning I finished testing my C++ implementation of linq. Here is an example of what it looks like. Your feedback is appreciated:

    Edit: I expanded the examples

    // Enumerable queries in C++
    // This program searches the current directory
    // And selects results using expression nodes
    
    #include <Codebot/Packages/System.h>
    #include <Codebot/Packages/Expressions.h>
    
    using namespace Codebot;
    using namespace Codebot::IO;
    using namespace Codebot::Text;
    using namespace Codebot::Expressions;
    
    void TestFileFind()
    {
      String SearchPath = "*";
      if (Application::Args().Length() > 0)
        SearchPath = Application::Args()[0];
      FileFind find;
      find.Search(SearchPath);
      auto query = Query(find)
        .OrderBy([] (FileResult a, FileResult b)
        {
          if (a.Attributes.Contains(FileAttribute::Directory))
          {
            if (b.Attributes.Contains(FileAttribute::Directory))
              return a.Name < b.Name;
            return true;
          }
          if (b.Attributes.Contains(FileAttribute::Directory))
            return false;
          return a.Name < b.Name;
        });
      ForEach(item, query)
        WriteLine(item.ToString());
      auto bytes = query
        .Select<int>([] (FileResult a) { return a.Size; })
        .Sum();
      WriteLine("{0|-31} total bytes\n", bytes);
    }
    
    void TestNumbers()
    {
      Array<int> numbers = {7, 4, 2, 8, 3, 6};
      auto q = Query(numbers);
      WriteLine("The first value is {0}",
        q.FirstOrDefault());
      WriteLine("The count value is {0}",
        q.Count());
      WriteLine("The sum value is {0}",
        q.Where([] (int i) { return IsOdd(i); }).Sum());
      WriteLine("The average value is {0}",
        q.Average());
      WriteLine("The min value is {0}",
        q.Min());
      WriteLine("The max value is {0}",
        q.Max());
      q = q.Where([] (int i) { return i > 2 && i < 8; })
        .OrderBy([] (int a, int b) { return a > b; });
      WriteLine("List: {0:list}\n", q);
    }
    
    void TestStrings()
    {
      Array<String> names = {"John", "Terrance", "Ralph", "Carl", "Stanley"};
      auto q = Query(names);
      WriteLine("The first value is {0}",
        q.FirstOrDefault());
      WriteLine("There are {0} items",
        q.Count());
      WriteLine("The sum is {0}", q
        .Where([] (String s) { return s.Contains("a"); }).Sum());
      WriteLine("The minimum value is {0}",
        q.Min());
      WriteLine("The maximum value is {0}",
        q.Max());
      q = q.Where([] (String s) { return s > "Mary"; })
        .OrderBy([] (String a, String b) { return a.Length() < b.Length(); });
      WriteLine("List: {0:list}\n", q);
    }
    
    int main()
    {
      return Application::Run({TestFileFind, TestNumbers, TestStrings});
    }
    
    
    /*
    Output:
    
    .                           Dir
    ..                          Dir
    Debug                       Dir
    .cproject                 14030  2012-04-12 14:41:33
    .project                   2508  2012-04-14 08:20:42
    Person.h                   1726  2012-04-12 22:59:28
    Widget.cpp                  871  2012-04-12 01:34:43
    Widget.h                   1663  2012-04-12 22:59:36
    main.cpp                   2415  2012-04-15 20:32:45
                              23213 total bytes
    
    The first value is 7
    The count value is 6
    The sum value is 10
    The average value is 5
    The min value is 2
    The max value is 8
    List: {7, 6, 4, 3}
    
    The first value is John
    There are 5 items
    The sum is TerranceRalphCarlStanley
    The minimum value is Carl
    The maximum value is Terrance
    List: {Ralph, Stanley, Terrance}
    
    */

  • User profile image
    c_str

    , sysrpl wrote

    ForEach(item, items)

     

     

    Why do you have your own ForEach function, and not using st::for_each, or a range based for loop?  I'm guessing it's because the collections your linq operators return don't have iterators, and thus are not compatible with the Standard Template Library.  Doing that is the very opposite of being friendly since you've eliminated a good portion of standard C++.

  • User profile image
    sysrpl

    @c_str:

    I am trying to create idioms whereby when code is written with my library it feels like a higher level language.

    int i = 0; std::for_each(nums.begin(), nums.end(), [&](int& n){  i += n; });
    
    // versus
    
    int i = 0; ForEach(n, nums) i += n; 
    
    // or simply
    
    auto i = Query(nums).Sum();

  • User profile image
    c_str

    again Why? What benefit does your library get from "feeling like a higher level language" ?   and no  feeling like a higher level language, is not a benefit since, it amounts to telling developers that in order to use your library they need to learn a new language.    

     

     

     

     

  • User profile image
    sysrpl

    @c_str: /ignore

  • User profile image
    Adam​Speight2008

    Since LINQ is a library of functions (incase of .net utilising IEnumerable and IEnumerator).

    How is different to learn a new library? @c_str 

     C and C++ is full of libraries developers have the potental to learn. 

    From a maintainability standpoint, the last two example @syspri provides are much simpler.

  • User profile image
    c_str

    @sysrpl /ignore <- see easy

    , AdamSpeight2008 wrote

    Since LINQ is a library of functions (incase of .net utilising IEnumerable and IEnumerator).

    Right, linq for .net utilizes what  already existed in .net.  Your point? 

    How is different to learn a new library? @c_str 

     C and C++ is full of libraries developers have the potental to learn. 

    Do you mean difficult?   It really depends on the library.  

    For example the multiple libraries  for C++ that support LINQ  that are compliment with the STL.

     

     

    From a maintainability standpoint, the last two example @syspri provides are much simpler.

    Really?  

    auto complete is going to work? 

    Effectively throwing away most of a language is going to help improve maintainability?

     

     

     

  • User profile image
    Sven Groot

    , sysrpl wrote

    I am working on a library for C++ which is small, has no dependencies other than itself (i'm not using boost), works on all platforms, and most importantly is as friendly as C#.

    Out of curiosity, how do you deal with unicode? This is always the thing that trips me up when trying to write cross-platform C++ code. On Windows, you must use wchar_t; using char is simply not an option. On Linux, wchar_t is very wasteful (4 bytes), unnecessary because char supports utf-8, and syscalls don't accept wchar_t string arguments. However, a lot of the std library string manipulation stuff doesn't actually work with multi-byte strings of char.

    The only way I've found to handle this is to take dependencies on things like ICU, which are rather big (~10MB).

    EDIT: On a sidenote, how does WriteLine work? It's either not typesafe (very, very bad) or your library only supports compilers with support for variadic templates (not very many at the moment).

  • User profile image
    sysrpl

    @Sven Groot: I've wrtten my own string class which stores strings as reference couted unicode strings. It has constructors to handle a few different string types, and as you know with c++ this allows you to construct an object with a simple assignment.

    Regarding WriteLine(), it is type safe, but I had to resort to writing many templated overloads

    WriteLine(const String& s);
    
    template <typename A0>
    WriteLine(const String& s, const A0& a0) {}
    
    template <typename A0, typename A1>
    WriteLine(const String& s, const A0& a0, const A1& a1) {}
    

    Etc ... I also did the same thing with my Format() function. There is a single object hierarchy structure to my classes with the root being class ValueType. ValueType provides a ToFormat(const String&) method which allows customization of how classes can be formatted.

    When you say Format("Hello {0:base64}", memoryBuffer), the Format() function parses out "base64" from the 0th argument and passes that to a0.ToFormat(format) ... where format holds the value "base64".

    In this way any class can provide it's own formatting by simply override ToFormat(). For the default types like int, bool, double, etc I provide default formatters, eg: Format("{0:$#.##|-20}", dollarsAmount). Might return: "                 $150.00"

  • User profile image
    Sven Groot

    , sysrpl wrote

    @Sven Groot: I've wrtten my own string class which stores strings as reference couted unicode strings. It has constructors to handle a few different string types, and as you know with c++ this allows you to construct an object with a simple assignment.

    That doesn't really answer my question. How are you storing the unicode strings? Which character type and which encoding? How are you handling conversions?

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.