HNHacker News
TopNewBestAskShowJobs

pfultz2

108 karma · joined November 17, 2012

submissionscomments
pfultz2··on Ask HN: Is C/C++ really faster than Python?
> One of the other differences is that Python tends to provide higher levels of abstraction than C/C++ code. So, doing something like a set intersection using x.intersection(y) may not necessarily be the fastest way.

C++ provides `set_intersection` as well: http://en.cppreference.com/w/cpp/algorithm/set_intersection

pfultz2··on C++: Uniquely addressed
`sum_f()` is calling the constructor, not the call operator(which would be `sum_f()(1, 2)`).
pfultz2··on The Sunset of C and C++
> It is even more likely that the user will use some serialization

Serialization is beyond the scope of what I am talking about. I am talking about reading bytes from a serial port.

> In that way, when using Async for network sockets, the SerialPortHW concept wouldnt be involved at all

But that is my whole point about Asio being split into smaller libraries such as Asio.Core, Asio.Network, and Asio.SerialPort. So you can use Asio.SerialPort without needing Asio.Network.

> in many occasions such extra async framework or the streams are not used.

You didn't read and write bytes to the serial port? What did you use it for?

> it would be in a different concept not included in the current project.

Its important for the library to implement the same "concepts"(or type requirements) for better consistency and composability(the only exception is iostreams which is horrible for both usability and implementations).

pfultz2··on The Sunset of C and C++
Well since Asio is generic there is no coupling between the interface and implementation. However, from a user perspective, they will most likely use both(even its just for sync reading), since Asio provides other utilities such as buffered reading and reading until a certain character is found.
pfultz2··on The Sunset of C and C++
BTW, Asio can be used without needing boost. In fact, the non-boost version is the actual maintained version. A script is applied to boostify it. It would be nice if Asio could be added to biicode.
pfultz2··on The Sunset of C and C++
> If we had something similar for C and/or C++ before boost was developed, I'd bet we had a SerialPort lib, independent, portable, efficient, focused and well tested, and BoostAsio would just depend on it, instead of developing inside that logic.

That's backwards. A SerialPort library would depend on Asio since it defines sync and async streams and the utilities built around that. It seems bad design for a SerialPort library to reimplement those basic utilities.

A dependency manager allows for finer modularity. So instead, Asio could be split into multiple libraries: Asio.Core, Asio.Serial, Asio.Network, etc. Its ok we have to bring in two smaller libraries instead of one large libary, since we have a dependency manager to handle this for us.

Also, a lack of a dependency manager in C++ has lead to developing of large monolithic libraries. So instead of managing dependencies, the library gives you everything plus the kitchen sink, so the user won't need to use another library. Of course, this is not scalable.

The other side-effect from a lack of a dependency manager in C++ is the developing of unnecessary header-only libraries(like Boost.Signals2). This just slows down compilation.

A real viable dependency manager in C++ will be a game changer, and recently. there is a lots of serious talk about it. In fact, there are like 3 difference talks that are directly related to dependency managing at C++Now 2015.

pfultz2··on One Man’s Quest to Rid Wikipedia of Exactly One Grammatical Mistake
> As a linguist, it is absolutely not an issue of grammar. The grammar is 100% correct. Nit-picking over the so-called "correct" definition has nothing to do with grammar.

This is true. Sentences without correct grammar are unparseable(like "he jumped didn't far"). A lot of people seem to confuse semantic or logic issues with grammar issues.

pfultz2··on Show HN: The Silicon C++14 Web Framework
Actually using [ZLang](https://github.com/pfultz2/ZLang), you could define them like this:

    #define IOD_DEFINE_SYMBOL(name) iod_define_symbol(name, _##name)
    #define IOD_S(name) s::_##name

    #define ZLANG_iod_define (IOD_DEFINE_SYMBOL)
    #define ZLANG_iod_s (IOD_S)
And then compiling with `-DZLANG_NS=iod` you can call it like this:

    // Define symbols
    $(define hello)
    $(define message)

    auto hello_api = make_api(

        // The hello world procedure.
        $(s hello) = [] () { return D($(s message) = "Hello world."); }

    );
Also, ZLang can be supported without a need for a dependency on ZLang.
pfultz2··on John Carmack: Functional programming in C++ (2012)
Thats cool. Here's the somewhat equivalent in C++:

    struct square
    {
        int x;
    };

    struct rectangle
    {
        int x;
        int y;
    };

    boost::variant<square, rectangle> to_shape(int x, int y)
    {
        if (x == y) return square(x);
        else return rectangle(x, y);
    }

    std::vector<std::pair<int, int>> unknowns = {
        {5, 4},
        {3, 2},
        {4, 4}
    };

    auto shapes = unknowns | boost::adaptors::transformed(boost::fusion::make_fused(to_shape));
Of course, the structs are not polymoprhic and can't be printed out. I just left that out because it requires a little more code to do that. Obviously with haskell its much less code.

I would very much be interested in the dependently typed version.

pfultz2··on John Carmack: Functional programming in C++ (2012)
I don't know about that link, but in general you would use a fix point combinator. The Fit library has a fix point combinator here:

http://pfultz2.github.io/Fit/doc/html/fix/

So you could implement a generic factorial like this:

    auto factorial = fit::fix([](auto self, auto x) -> decltype(x) 
    { 
        return x == 0 ? 1 : x * self(x-1); 
    });
pfultz2··on Rust lifetimes: Getting away with things that would be reckless in C++
Actually, it should be written more like this:

    template<typename T>
    std::pair<T, std::vector<std::string_view>>
    tokenize_string(T &&str);
So then it uses `std::forward` to conditionally move the string for rvalues, and `T` will be a reference to the original string for lvalues.
pfultz2··on Pipable functions in C++14
> It is possible to implement it in C++, but the implementation winds up being ridiculously complicated.

It may be more verbose in C++, but its not more complicated, and its definitely not "ridiculously" complicated.

> I just implemented something very similar in Common Lisp and it wound up being incredibly simple.

Maybe for you as a lisp programmer, but for me I find the lisp code incomprehensible.

pfultz2··on Pipable functions in C++14
> To me, the first is much easier to read, but perhaps my mind has been warped by functional programming languages.

Actually, pipable is very similiar to haskell's `$` function. So a similiar thing is done to improve readability of chaining functions in functional languages as well.

> At any rate, the thing that's strange about it is how verbose and hackish everything is. It's obvious the language wasn't designed to do anything like this, so the only way you can incorporate features like this is with unnecessarily obtuse and complex blocks of code.

How is the code obtuse and complex? I am using fairly straightforward techniques. I understand that I am working around the lack of perfect capturing for lambdas, but it is not complicated or clever the workaround.

pfultz2··on Pipable functions in C++14
> The issue with extending operators in this fashion is that you're violating the principle of least surprise.

It is really common for the `|` operator to be used for piping functions together. There are several libraries that support it: [Boost.Range](http://www.boost.org/doc/libs/1_56_0/libs/range/doc/html/ind...), [PStade Oven](http://p-stade.sourceforge.net/oven/doc/html/index.html), [Linq](http://pfultz2.github.io/Linq/), and [Streams](http://jscheiny.github.io/Streams/). Plus, its even being proposed as part of the ranges library in future C++: https://github.com/ericniebler/range-v3/blob/master/doc/D412...

So just as the bit shift operators in C++ also have the meaning of streaming in and out, the bitwise or also has the meaning of pipe, so this is should not at all be a suprise.

pfultz2··on Pipable functions in C++14
> You can write good C++ that more or less resembles Java

That doesn't sound like good C++. Making everything a pointer or implementing polymorphism through pointers makes the code harder to reason about, and makes tighter coupling between interfaces and implementations.

pfultz2··on Pipable functions in C++14
I disagree. It makes it very simple to make a pipable function, you just wrap the function object in pipable. There's nothing complicated about using it.

Futhermore, doing it by hand is not as simple, but still not very complicated. This just helps alleviate the boilerplate in defining pipable functions.

pfultz2··on Pipable functions in C++14
In practice, I've yet to see a problem.
pfultz2··on Pipable functions in C++14
How is it self-evident?
pfultz2··on Pipable functions in C++14
Well this can help when you have many nested functions that are chained together, for example, like this:

    auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; });
Because of nesting this can be hard to read(and even write). If we can make the function pipable, the above can be written like this:

    auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; });
Which it is easier to see what this does. So this blogpost goes over how to implement an utility that will take care of the boilerplate involved in writing a function that can be pipable.

It does take advantage of a lot of C++14 features to implement this such as such as vardiac templates, rvalue reference, generic lambdas, and auto type deduction, but those feature actually make it simpler. I have in the past implemented something similar for C++03 and the code looks awful with way too much noise.

> this is madness.

Why do you consider this madness?

Maybe my mind has already been warped by C++.

pfultz2··on What happens when you search 'ugly hack' in GitHub
There is a library in C++ called Web Transaction Framework(WTF): https://github.com/belzano/WTF
pfultz2··on Interpreting when Compiling, or an Alternative Understanding of Lambdas in C++11
You should check out metashell then: https://github.com/sabel83/metashell
pfultz2··on Lazy sequences in idiomatic C++ using iterators
This can already be done using Linq in C++, which is range-based rather than iterator based:

    auto q = LINQ(
                from(z, boost::irange(1, std::numerical_limits<int>::max()))
                from(x, boost::irange(1, z))
                from(y, boost::irange(1, x))
                where(x*x + y*y == z*z)
                select(boost::fusion::make_vector(x, y, z))
            );
    for(auto&& x:q | linq::take(10)) std::cout << x << std::endl;
Hopefully, once we get ranges in the C++ standard library, we can add list comprehension to the language, perhaps something like this:

    auto q = [
                for (z, std::irange(1, std::numerical_limits<int>::max()))
                for (x, std::irange(1, z))
                for (y, std::irange(1, x))
                if (x*x + y*y == z*z)
                boost::fusion::make_vector(x, y, z)
            ];
    for(auto&& x:q | std::take(10)) std::cout << x << std::endl;
pfultz2··on C++ – From goto to std::transform
Or using [Linq](https://github.com/pfultz2/Linq):

    vector<int> squareVec(const vector<int>& v)
    {
        return LINQ(from(i, v) select(i*i)) | linq::to_container;
    }
And also filtering out odd elements:

    vector<int> squareVecFiltered(const vector<int>& v)
    {
        return LINQ(from(i, v) where(i % 2 == 0) select(i*i)) | linq::to_container;
    }
pfultz2··on How to abuse a C++ compiler?
> Type-level meta programming is still confined to using templates though...

Which there is less of need for this, since we have better type deduction in C++11 and 14 as well.

pfultz2··on Looking at C++14
> If in is an operator and overloaded by std::vector, then it's easy to implement.

However, searching a `std::vector` is not really part of its implementation. So, requiring every single range to overload an operator for trivial searching is not good, especially since there are only a few containers that have non-trivial searching.

Instead, the range-based find could be specialized for containers that have non-trivial searching instead(perhaps it calls a `.find` method if available otherwise it does trivial searching).

In [Linq](http://pfultz2.github.io/Linq/), the find is specialized for mapped containers and for strings(it doesn't check for a `.find` method since that can't be done on msvc). So you can search a `std::vector` like this:

    std::vector<int> v = { ... };
    auto it = v | linq::find(123);
And you can also search the keys of a map like this:

    std::map<int, std::string> m = { ... };
    auto it = m | linq::find(123);
Plus, it has a contains function, so you can do this:

    if (v | linq::contains(123))
Or this:

    if (m | linq::contains(123))
There really is no need for a new operator just for this, since a function will suffice. However, if you really inclined to this, you could always write your own [named operator](https://github.com/klmr/named-operator)(although I don't know if it would become standard):

    if (123 <in> v)
pfultz2··on Looking at C++14
> But hey, at least they have a Turing-complete template language to go with their macros! Priorities, right?

And with that you can build reflection:

http://stackoverflow.com/a/11744832/375343

pfultz2··on Brainfuck interpreter written in the C preprocessor
So the expression of unbounded recursion can be expressed like this:

    #define EMPTY()
    #define DEFER(id) id EMPTY()

    #define FOREVER() \
        ? \
        DEFER(FOREVER_INDIRECT) () ()
    #define FOREVER_INDIRECT() FOREVER
This will print out `?` infinitely. Here is the machinery to evaluate the expression:

    #define EVAL(...) A(A(A(__VA_ARGS__)))
    #define A(...) B(B(B(__VA_ARGS__)))
    #define B(...) C(C(C(__VA_ARGS__)))
    #define C(...) D(D(D(__VA_ARGS__)))
    #define D(...) E(E(E(__VA_ARGS__)))
    #define E(...) __VA_ARGS__

    EVAL(FOREVER())
Which will expand to something like this:

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? FOREVER_INDIRECT () ()
Well at the end we see `FOREVER_INDIRECT () ()` which means the algorithm can expand further, so we could just apply more scans:

    EVAL(EVAL(FOREVER()))
And we get more `?`. And we didn't have to change the expression of the algorithm just the machinery to evaluate it.

This is why I say the C preprocessor can act as Turing-complete, because the expression and machinery, while separate, is both in the language itself. This is different than BlooP, which the only possible way to express Turing completeness is through an external source(such as an input file).

Of course, for all practical purposes, the C preprocessor is near Turing-complete enough. The whole goal of the preprocessor is to output a file, we don't need it run indefinitely. We can express almost any useful algorithm, and if it reaches the recursion limit, we just simply apply more scans.

← PreviousPage 2 of 2