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.
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...?
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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...)
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).
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.
// 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.
68 comments
[ 5.5 ms ] story [ 117 ms ] threadI don't like decisions like implicit << and >> for cin, cout. Too much magic for me.
(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.
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?
Problem solved (at least partially) in other languages by requesting the `this` or `self` to be explicitly mentioned.
Was it ever widely uses? I was glad we did it and am glad to see that someone appreciated it!
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.
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.
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.
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.
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).
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.
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.
For deduced types you can do
And not have to mess with known.For multiple return types you can do structured binding and make_tuple.
Where func is Using a loop, you can use boost::irange For the output stuff, you can use fmt (https://github.com/fmtlib/fmt/blob/master/README.rst) which is on track for standardization. Overall most of the problems that CPY implies have pretty good solutions in C++ without the pain of another pre-processorMyDef 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
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.
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.
- 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?
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).
return {1, "42", '2'};
seems like a fair trade-off to me. Btw, this is already implemented from c++11.
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.
I.e. nesting information is repeatedly encoded into every line. Nope, no redundancy here, move along!
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.
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++.