68 comments

[ 5.5 ms ] story [ 117 ms ] thread
This suggests that large parts of C/C++ can be 'simplified', which I think is a falacy, resulting in CPY to be a proper subset of C/C++. Already the introduction of a keyword like 'known' shows that the simplification is not as simple. I suspect that many new keywords needs to be added to support the full scope of C/C++ within CPY.
If you think of cpy as high level glue code for "pure" c++ functions, this might work.

I don't like decisions like implicit << and >> for cin, cout. Too much magic for me.

Honest question, why do you consider implicit << and >> for cin,cout to be too much magic?
It's a misappropriation of an operator. C had a "definition" for them, and this completely disregards that definition. It's one of the biggest things people cry about when custom operators are brought up in other languages, because C++ really dropped the ball here in using operators in a sane way.
And yet we had a few decades of magic 0 literals that transmute into pointers until we finally got a proper nullptr. There's some grade A impurity built directly into the core language and hard to avoid unlike the std lib.
IMO, the usefulness of things like CPY is being easy to read, without too much noise or boilerplate.

(cout "hello") confuses the hell out of those with some C++ experience. It changes semantics if a known object for little gain (cout is callable, a statement)? Like someone else said, a "printout" new statement or function would be better.

It's the kind of things that makes me question the design of the whole language.

Yeah that implicit operator insertion is a bit odd... but things like automatically inserting an "auto" into declarations seems fair. I'd prefer maintaining "this->" to the "known" keyword.
Yeah, it would make more sense to just add the new line printing shorthands and leave cin/cout/cerr alone. Overloading "for" as a loop over a range of integers also seems questionable, and it seems you'd need to pollute the scope outside of the loop each time you don't want to start from 0.
Within a local context (where corner cases can be excluded) large parts of C/C++ can be simplified. Expand to a general context, I think too it is a fallacy. Even when in theory the compile time can determine all the types and ambiguity, including all the corner cases makes learning the language very complicated. However, as a preprocessor, it does not need to work beyond local context as C/C++ is still there to deal with general corner cases.
> Already the introduction of a keyword like 'known' shows that the simplification is not as simple.

Don't think that one is actually needed, simple static analysis should be able to tell one variable is just an alias of another -- which (presumably) they already do to figure out the variable types.

The reasoning behind it is also kind of confusing, used to tell the preprocessor the variable is in local scope as opposed to...?

--edit--

Or maybe it's used for class local variables?

I actually suspect that `known` solves an existing problem of C++, in which you can't be sure whether a name is that of an argument, a local variable or a field.

Problem solved (at least partially) in other languages by requesting the `this` or `self` to be explicitly mentioned.

One basically needs to implement perfect C/C++ parser in order to accomplish this with sufficient accuracy. Does CPY implement that? No.
libclang to the rescue! (which CPY doesn't use)
Very interesting! On a slightly related note, I'd personally love to see more "C++ variants" (for lack of a better term) out there. By that, I'm referring to ecosystems/languages that are essentially a simplified/abstracted/modernized version of C, but ones that also still allow you to "break out" back to classic C at will. Thank you for sharing.
Objective-C? D?
Correct me if I'm wrong, but Objective-C's ecosystem is very much Apple-centric. Things in that regard have only started changing fairly recently.
It's always been cross-platform; GCC has supported it for decades. Apple's Cocoa libraries are Apple-centric, but there have long been alternatives like GNUStep.
Apple-centric? The libraries in iOS and macOS, for sure. However, the language itself can be used anywhere where GCC is available.
FWIW Objective-C predated Apple (or NeXT which adopted it and brought it to Apple) and was pared by Smalltalk.
Also (sadly) Objective-C++.
A few years ago I ran into Steve Naroff in a diner and that’s one of the first things he said to me: ‘I’m still amazed we got Objective-C++ to work’ (for both political and technical reasons).

Was it ever widely uses? I was glad we did it and am glad to see that someone appreciated it!

"CPY" seems to concentrate on syntax. I would not call an expressive syntax "redundant". From my perspective, redundancy in programming arises due to the lack of programming skills or a good preprocessor, especially probably in the C/C++ world where codes are more likely tailored for speed.

The problem with (essentially) replacing the C/C++ syntax with a more lightweight (pythonic) one was already mentioned here in the threads: There will be a lot of corner cases which the CPY compiler does not understand.

Redundancy in C++ chiefly arises from textual inclusion as the mechanism for communicating about the capabilities of other modules. It's not because of a bad preprocessor or programming skill.
Can you give an example of what you refer to?

In my impression, most of the redundancy in the C world comes from the header/code seperation, i.e. all the signatures are repeated. Another level of redundancy was added by C++ templates where one quickly needs to write lengthy expressions, but the newer C++ standards resolved much of that thanks to the "auto a = b" type keyword or the "use X = Y" definitions.

Note that "CPY" addresses none of these issues.

Grandparent's use of "textual inclusion" was just a fancy way of saying "header/code separation". As I read it you guys agree.
I must be one of the few that like the header/code separation. When dealing with plain text, I get a more concise view of what is going, as well as a view of what should be used by other modules.
The problem with the separation is mainly with making changes to code, not with reading the code as it currently exists.

This problem is avoided at no cost in Go, where an external tool can be used to get the same concise view of all of the function types that you like, without needing to manually create and update header files.

Writing code takes effectively no time, reading and reasoning is the primary concern (for me anyway).
My first question would be: how much experience do you have with other languages? Because C and C++ are the only two major languages that have this separation, and everybody else seems to get on fine without it. If you're knowledgeable about other languages, then fair enough, preferences vary. If not, I ask you to consider the possibility that you only like it because it's familiar.
I have been programming for 20 years. C++/Python/JavaScript/Visual Basic/Objective C/Java.

Most of my work these days is in JavaScript (Node).

I agree it is annoying to write, but I find well documented headers to be more usable than the other environments, if the project is large enough that I cannot read everything.

What do headers add that shouldn't be inferred from the code?
Improve compilation speed.
I think this could be more interesting if it was a superset of C++ that allowed for brevity in some cases. Something that could:

1. Act as a preprocessing step for any existing C++ file

2. Output perfectly servicable C++ in the case where you want to move away.

Additions like the "known" keyword make this look badly thought out.

In any case, the weakness of C++ in most cases is compilation speed, not typing speed. Even in the ideal case I can't see this adding more value than it detracts (preprocessing speed + buildsystem complication + extra dependency).

One nice feature is writing your class interface/implementation all as one and the system splitting it into .h .and .cpp. Especially if it can be set to do things like the "pimpl" pattern.

I'm probably a minority in the C++ world, partially because I don't have a powerful IDE, but I tend to write everything in .h and only split it out later on, which gets annoying.

C++ is (hopefully) getting a proper modules system. So this should eventually be a problem of the past.
The work required to split things into .h and .cpp files is trivial and only needs to be done once. There's no need for a tool to simplify this 30sec step.
I write template-heavy code, so most ends up being in headers anyhow.
If compiler times and ordering of declarations were easier to handle, I would probably change my code to live in headers only. Keeping headers and sources in sync is annoying. And the compiler also gets amazing optimization opportunities if it can see all of the implementation code.
As far as optimizations go, LTO (Link Time Optimization) solves a large part of that problem
Simply disabling variable implication actually fixes that and removes the need for the "known" keyword. Doing that would turn CPY into a valid preprocessing step for any existing C++ file. I'm currently working on a way enable or disable certain functionalities according to your preference.

It is in fact able to output perfectly servicable C++ code by simply using a flag (-ex)

CPY also helps with the compilation speed problem by doing a makefile-like compilation, were it only recompiles edited parts of the code.

It can also be used upon complete c++ projects and it would only actually execute the compilation step.

Just added a compilation flag to completely disable variable implication, this removes the need to ever use the "known" keyword
So CoffeeScript meets C++?
Implicit variable declaration, multi-value return, so semicolon / parenthesis... sounds a lot like Golang.
(comment deleted)
Sounds a lot like most languages designed during the last 20 years (or 50 years if you include academia), actually :)
It seems a lot of cpy features are covered by C++ and/or libraries.

For deduced types you can do

    auto a = 10;
    auto b = a;
And not have to mess with known.

For multiple return types you can do structured binding and make_tuple.

   auto [a,b] = func();
Where func is

   auto func(){return std::make_tuple(1.5, 3);}
Using a loop, you can use boost::irange

    for(auto i: irange(0,10)){

For the output stuff, you can use fmt (https://github.com/fmtlib/fmt/blob/master/README.rst) which is on track for standardization.

    fmt::print("{} {} {} ", a,b,c);
Overall most of the problems that CPY implies have pretty good solutions in C++ without the pain of another pre-processor
This is very true. It would be elegant if "CPY" compiled to these newer C++ features (or boost).
I use MyDef to achieve most of features `cpy` is trying to introduce:

    # t.def
    page: t, basic_frame
        module: c

        a=10
        b=a
        $dump a, b

    $ mydef_run t.def
    PAGE: t
      --> [./t.c]
    gcc -std=c99 -O2 -o./t ./t.c  && ./t
        :a=10, b=10
I didn't introduce `known` keyword. If I want straight C code passthrough I simply append semicolons.

MyDef is a general preprocessor, so I need the page and module declarations to tell what file and types I am generating. It always shows the command it actually runs, so there is no magic. And it does not pretend to replace C. It always produces `.c` file for debugging or merging with existing workflow.

As long as MyDef is strictly preprocessor, it is purely complementary. If you prefer straight C/C++ at places, just use straight C. If you have questions on what MyDef syntax does, just read the C output.

It does pose some restrictions on the C I write. I do not write long statements in multiple lines and rely on my editor to softwrap the lines. Long lines are difficult to read anyway with or without splitting and soft-wrapped lines are not much difficult to read anyway. I often can avoid long lines by refactoring. In rare cases that lines still end up too long, I leave it as is to remind me that it needs further work.

Sometimes I need import third-party raw c code during a progress of refactoring or code digesting. MyDef has template construct to quote them directly.

MyDef: https://github.com/hzhou/MyDef output_c: https://github.com/hzhou/output_c

Last year I learned about MyDef. I like the basic concept, but it did not fit my use case unfortunately. I also am not well versed in Perl and at least at the time the documentation of the internals was too sparse for me, so I was not able to adapt it to fit my needs. But I do think people should look more into the idea of using preprocessors like MyDef and CPY.

I, like everyone else who has the need, wound up writing my own C++ preprocessor. I think there is a lot of merit to the idea of having a low level base language which is machine independent and using a high level syntactic sugar over it.

I lately do realize that to be comfortable with MyDef, one need be versed in three languages -- the language you are writing, MyDef, and Perl. People seem more welcome to the idea of having new language replacing the old rather than a solution of taking advantage of existing tools. I think this is the same barrier of Knuth's literate programming. Despite how I find how much MyDef make life easier, others will not see the same if not ready.

What I am hoping for is conversation. With conversation, I can have the motivation for documentation and others can understand and the barrier may dissolve.

What about debugging? Looks like it's Linux-only.
A superset of C++ would be unwise: same issues as those C++ inherited from C! I imagine something similar to CPY could be created for different domains like:

- Pure functional style (Haskell subset that is usable from C++)

- GC'd higher level gluing code (calls C++ but maybe not necessarily callable from it)

Am sure others are more qualified for suggestions, but I imagine other subsets like some for interfacing with representing relational data querying, different concurrency models (like Go, Erlang...)

Is this a sane suggestion?

So, just syntactic sugar for frequent use cases (stdin/stdout with iostreams, returning tuples)?
The feature I like here is an old Common Lisp feature: accepting as many returned values as you’d like. C/C++ allows you to accept 0 returned values without special syntax, but in all other cases you have to be careful. Whereas in CL you can return as many values as you like and the caller can accept as few of them as it likes with no extra overhead.

In c++17 you can sort of do that but you still have to unpack the whole tuple (and the caller has to make one instead of simply doing ‘return a, b, c;’ to return three values. I understand why the C++ committee didn’t make this change (it would break valid programs, though there are likely few to none of them).

Having to write :

return {1, "42", '2'};

seems like a fair trade-off to me. Btw, this is already implemented from c++11.

Damn it.. this was my idea. I was going to call the language "raw" x(
Hey, cool project!

I’m not sure I agree that the input is more readable, but good nonetheless.

If I had the choice, I’d move the syntax closer to Haskell do-notation than Python, but that’s just me.

> ... without redundancy. Curly brackets are implied from identation

I.e. nesting information is repeatedly encoded into every line. Nope, no redundancy here, move along!

The redundancy that matters to me is writing code for yet another serialization or pretty printer. I couldn't care less about syntax.

I am a firm believer that the compiler should do as much work as possible, no matter the time it takes. I think the compiler ought to generate default pretty printers for structs and unions on its own, that may be overridden. Same for serialization formats and so on. The C++ compiler has more than enough information to make programmers lives more convenient at no added runtime expense, and I would be very willing to eat the compile time cost.

I wound up having to write my own preprocessor for C++ to automatically generate structure metadata for an embedded application. I wish there was more of a push for general preprocessors in this direction.

(comment deleted)
Reminds me of Groovy vs Java ... although seems limited to syntactic sugar mostly rather than adding functionality.

    // CPY loop to iterate from a down to b, c at a time
    for i a b -c
    
    // For comparison: pure C++11 and boost
    for(auto i : irange(a, b, -c))
After CPY removes redundancy to this extent, sure there's still enough information for a compiler to parse this, but there's not enough information for me to parse this. Remember that you spend a lot more time reading code than pressing buttons on the keyboard, so the number of characters you need to type is pretty irrelevant to your productivity.

Maybe I'm getting too caught up on the name, which suggests a Python-like experience, which CPY definitely not. If anything it is less like Python than C++! Sure it has the indentation vs braces thing, but that is probably one of the least important reasons why Python is so readable. A more important reason is the excellent visual cues in the syntax, and forcing you to spell things out e.g. with named parameters; "explicit is better than implicit". CPY seems to strip out visual cues, as shown above, by its four print functions that are cryptically called !, !!, ? and ??, and by the fact that it's even harder to spot function declarations in CPY than in C++.

I would love to be able to write C++ without having to write header files, there is an insane amount of redundancy between header/sources in any C++ project. I don't even care about anything else, that would be the most important thing this tool should support flawlessly.
You could simply write header-only code (see keyword 'inline').