- Announce: Macro-Magic (MM), a macro to invoke a macro on up to 21 arguments - 1 Update
- does the language guarantee `explicit specialization' will generate the code ? - 3 Updates
- [abot] some thought on os fundaments - 2 Updates
- Back to BCPL: using lambdas to do e.g. loops within expressions - 1 Update
- aborts at dl_main (in /lib64/ld-2.22.so) - 1 Update
- decision making tree for a euler walk - 1 Update
- Silly undead code. - 2 Updates
- "Distinguishing between maybe-null vs never-null is the important thing" - 1 Update
- A baffling bug in my bignumber code (expressive C++ dialect) - 3 Updates
- Is this N_TIMES loop macro safe for all arguments? - 1 Update
- Overloading global function in a class - 3 Updates
- copy-on-write - 2 Updates
- Idioms reference - 3 Updates
- A baffling bug in my bignumber code (expressive C++ dialect) - 1 Update
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 01:59PM +0100 Macro-Magic (MM) is a header-only library with a macro MM_APPLY to invoke a specified macro on each of up to 21 arguments. There's also MM_APPLY_WITH_FIXED_ARG to pass a specified fixed first argument to the invoked macro, and there's MM_NARGS to count the number of arguments passed to a variadic macro. And MM_CAT to concatenate with expansion. GitHub repository: <url: https://github.com/alf-p-steinbach/Macro-Magic/> This is pretty old code, from at least back in 2013 or perhaps years earlier than that, based on an original __VA_NARG__ macro by Laurent Deniau that he posted on 17 January 2006 to [comp.std.c]. <url: https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c/d-6Mj5Lko_s> I've modified it to work with Visual C++. I see now that I posted it on GitHub that I forgot to add a license, but it's Boost license 1.0. Cheers, & enjoy!, - Alf (hoping there are not too many bugs: code like this touches on an area where compilers are generally not standard-conforming!) |
Shiyao Ma <i@introo.me>: Feb 24 11:24PM -0800 Hi, Does the standard say, if a template is explicitly specialized, the compiler will emit the machine code? If so, what part? http://eel.is/c++draft/temp.spec doesn't seem to force the compiler to generate the code. I'd afraid this might result a link error. Regards. |
Paavo Helde <myfirstname@osa.pri.ee>: Feb 25 10:49AM +0200 On 25.02.2017 9:24, Shiyao Ma wrote: > If so, what part? > http://eel.is/c++draft/temp.spec doesn't seem to force the compiler to generate the code. > I'd afraid this might result a link error. Explicit specialization and explicit instantiation are different things. Only the latter forces instantiation (aka "code generation" in your terms). |
Shiyao Ma <i@introo.me>: Feb 25 04:46AM -0800 My question has nothing to do with `explicit instantiation' I was saying `explicit specialization', not `explicit instantiation'. |
Prroffessorr Fir Kenobi <profesor.fir@gmail.com>: Feb 25 03:53AM -0800 [abot = a bit of topic, i post here becouse i suspect there may be a bit of practical programmers and os is practical problem for practical programmers /though i know it is not on c++ if not interested skip it] I recently seen some very lame person that was sitting against fresh instalation on winxp (on some older machine) this person was barely able to use a mouse and was acting this way that opened every dialog/program and clicked everything for example she tried to rename documents-and-settings folder etc such usage would be able to take down this machine in a couple of minutes i began then to thing if it would be really harder and not logical to put some basic security in such system and what it would need to be wasnt it to be just sufficient to put whole os instalation into one folder (may it be /WINDOWS) where basic user would not be able just to delete or change files? other thing is how rest of the filesystem should look like |
fir <profesor.fir@gmail.com>: Feb 25 04:23AM -0800 W dniu sobota, 25 lutego 2017 12:53:19 UTC+1 użytkownik fir napisał: > os instalation into one folder (may it be /WINDOWS) where basic user would not be able just to delete or change files? > other thing is how rest of the filesystem > should look like as to this filesystem i see that there should be such points to fulfill 1) every program installed should have accesto some disk space where it could save its data 2) system could eventually provide support for many users/accounts but im not sure if not more things should be provided (? - some hints?) if speaking how to implement those above points it seem that it could be done if each user would have ovn folder near the root \WINDOWS \USERS\ala \USERS\john programs should probably have their own foler \PROGRAMS and as a storage space programs probably should vrite its settings to some location in users space i dont know linux but linux seem to do something like that (though maybe a bit more complicated and im not sure if this additional complication is ok) windows also try to do it like that but windows makes overall trash imo (windows xp for example adds some 4 additional 'account folders' default-user loca-service network-service and all-users besides the one which i could see as my user flder - besides the content of this folders also makes a feeling of trash) |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 01:15PM +0100 I'm not sure, but I think BCPL, which C was based on, supported control structures within expressions; at least had curly braces in expressions? Anyway, after C++11 and C++14 we can now do that in C++: #include <p/expressive/use_weakly_all.hpp> // // #define $invoked $e::impl::Gurkemeie{} % []() // #define $invoked_with( instantiation ) \ // // $e::impl::Gurkemeie{} % []( decltype( instantiation ) = instantiation ) #include <iostream> $use_weakly_all_from( std ); struct Mutex { ~Mutex() { cout << "Mutex::<destroy>" << endl; } Mutex() { cout << "Mutex::<init>" << endl; } }; struct Library_usage { ~Library_usage() { cout << "Library_usage::<destroy>" << endl; } Library_usage() { cout << "Library_usage::<init>" << endl; } }; struct Tracing { ~Tracing() { cout << "Tracing::<destroy>" << endl; } Tracing() { cout << "Tracing::<init>" << endl; } }; $just { $let sum = $invoked{ int s{}; for( int i = 1; i <= 8; ++i ) s += i; return s; }; cout << sum << endl; cout << endl; $invoked_with( Tracing{} ) { cout << "Hum!" << endl; }; } Here the % operator function is found via Argument Dependent Lookup and invokes the specified lambda, producing an expression return value via C++14 automatic return type deduction, and that works with MSVC 2015. :) Cheers!, - Alf |
pedro1492@lycos.com: Feb 25 02:10AM -0800 I have an OSS package that includes 5 c++ programs. It worked fine under various old linux distros. Now under SLED12 all these programs abort, using both Intel and GNU compilers. The package also includes some Fortran90 programs, which still run okay. Valgind shows that the abortees load many dynamic libraries, then ==8529== Jump to the invalid address stated on the next line ==8529== at 0x0: ??? ==8529== by 0x40036F1: dl_main (in /lib64/ld-2.22.so) ==8529== by 0x4016304: _dl_sysdep_start (in /lib64/ld-2.22.so) ==8529== by 0x40049E9: _dl_start (in /lib64/ld-2.22.so) ==8529== by 0x4001157: ??? (in /lib64/ld-2.22.so) ==8529== Address 0x0 is not stack'd, malloc'd or (recently) free'd So it appears to me it is still in the loader, and has got a null address from god knows where. What is dl_main? Is it when the loader finally jumps to the start of the main program? |
ruben safir <ruben@mrbrklyn.com>: Feb 25 01:12AM -0500 On 02/24/2017 05:11 AM, ruben safir wrote: > I'm just not quiet solving this. I think it walked the tree correctly > but my tunnel count is a wee bit off. ~~~~ {.cpp} #include <iostream> #include <unordered_map> #include <unordered_set> #include <vector> #include <stack> #include "assertions.hh" #define LOG_LEVEL LOG_TRACE #include "debug-log.hh" using namespace std; // Represent an undirected graph using hash maps and sets. The vertex // type can be anything that supports equality and hashing. template<typename vertex> struct graph { void add_edge(vertex v1, vertex v2); void remove_edge(vertex v1, vertex v2); int degree(vertex v); vector<vertex> euler_path(); ostream& show(ostream&); private: typedef unordered_multiset<vertex> neighbors; typedef unordered_map<vertex, unordered_multiset<vertex>> adjancency_map; adjancency_map adjacency_map; void add_directed(vertex v1, vertex v2); void remove_directed(vertex v1, vertex v2); }; // Print the graph's adjacency list template<typename vertex> ostream& graph<vertex>::show(ostream& out) { for(typename adjancency_map::iterator i = adjacency_map.begin(); i != adjacency_map.end(); i++) { out << i->first << ':'; for(typename neighbors::iterator j = i->second.begin(); j != i->second.end(); j++) { out << ' ' << *j; } out << '\n'; } return out; } template<typename vertex> ostream& operator << (ostream& out, graph<vertex>& g) { return g.show(out); } // Add an undirected edge. template<typename vertex> void graph<vertex>::add_edge(vertex v1, vertex v2) { add_directed(v1, v2); add_directed(v2, v1); } // Add directed edge (private). template<typename vertex> void graph<vertex>::add_directed(vertex v1, vertex v2) { typename adjancency_map::iterator i = adjacency_map.find(v1); if( adjacency_map.end() == i) { adjacency_map[v1] = neighbors{v2}; } else { i->second.insert(v2); } } // Remove undirected edge. template<typename vertex> void graph<vertex>::remove_edge(vertex v1, vertex v2) { remove_directed(v1, v2); remove_directed(v2, v1); } // Remove directed edge (private). A little tricky because just doing // .erase(v2) removes *all* the matching vertices from the multiset. // Using find then .erase in the iterator solves that. template<typename vertex> void graph<vertex>::remove_directed(vertex v1, vertex v2) { typename neighbors::iterator i = adjacency_map.at(v1).find(v2); adjacency_map.at(v1).erase(i); } // Return the degree of given vertex. template<typename vertex> int graph<vertex>::degree(vertex v) { return adjacency_map.at(v).size(); } // Calculate and return an euler path. Algorithm basically taken from // here: http://www.graph-magics.com/articles/euler.php // // NOTE: this is destructive -- it removes edges from the graph. So if // you need the graph afterwards, make a copy. template<typename vertex> vector<vertex> graph<vertex>::euler_path() { // If all vertices have even degree, choose any of them. typename adjancency_map::iterator i = adjacency_map.begin(); vertex curr = i->first; // If there are exactly 2 vertices having an odd degree: choose one // of them. This will be the current vertex. unsigned num_odd = 0; for( ; i != adjacency_map.end(); i++) { if(degree(i->first) % 2 == 1) { num_odd++; curr = i->first; } } log(LOG_INFO, "There were " << num_odd << " odd-degree vertices."); vector<vertex> path; stack<vertex> stack; if(num_odd != 2 && num_odd != 0) { log(LOG_ERROR, "Sorry, no Euler path exists."); return path; } log(LOG_DEBUG, "Starting at " << curr << '\n' << *this); // Repeat until the current vertex has no more neighbors and the // stack is empty. while(degree(curr) > 0 || stack.size() > 0) { // If current vertex has no neighbors, if(degree(curr) == 0) { log(LOG_DEBUG, "* Adding " << curr << " to path."); // Add it to path path.push_back(curr); // Remove the last vertex from the stack and set it as the // current one. curr = stack.top(); stack.pop(); log(LOG_DEBUG, " New curr is " << curr); } else { // Add the vertex to the stack log(LOG_DEBUG, "* Pushing " << curr << " to stack."); stack.push(curr); // Take any of its neighbors, remove the edge between selected // neighbor and that vertex, and set that neighbor as the // current vertex typename neighbors::iterator n = adjacency_map.at(curr).begin(); log(LOG_DEBUG, " Removing " << curr << " <-> " << *n); remove_edge(curr, *n); curr = *n; log(LOG_TRACE, *this); } } path.push_back(curr); return path; } // Grr why isn't this just in the standard library I have to write it // every damn time. template<typename T> ostream& operator << (ostream& out, const vector<T>& vec) { for(unsigned i = 0; i < vec.size(); i++) { if(i > 0) { out << ", "; } out << vec.at(i); } return out; } graph<char> rubensburg_demo() { // Set up graph graph<char> g; g.add_edge('A','B'); g.add_edge('A','B'); g.add_edge('A','E'); g.add_edge('A','F'); g.add_edge('A','G'); g.add_edge('B','E'); g.add_edge('B','F'); g.add_edge('C','D'); g.add_edge('E','C'); g.add_edge('E','G'); assert_eq(5, g.degree('A')); assert_eq(2, g.degree('G')); assert_eq(1, g.degree('D')); return g; } graph<int> even_degree_demo() { graph<int> g; g.add_edge(13, 16); g.add_edge(13, 18); g.add_edge(16, 18); assert_eq(2, g.degree(13)); assert_eq(2, g.degree(16)); assert_eq(2, g.degree(18)); return g; } // This is the actual Königsberg graph, for which no Euler path // exists. graph<string> impossible_demo() { graph<string> g; g.add_edge("austin", "baltimore"); g.add_edge("austin", "baltimore"); g.add_edge("austin", "dallas"); g.add_edge("baltimore", "chicago"); g.add_edge("baltimore", "chicago"); g.add_edge("baltimore", "dallas"); g.add_edge("chicago", "dallas"); return g; } int main() { cout << "======= Even-degree test\n"; graph<int> g0 = even_degree_demo(); cout << g0.euler_path() << '\n'; cout << "======= Odd-degree test\n"; graph<char> g1 = rubensburg_demo(); cout << g1.euler_path() << '\n'; cout << "======= Impossible test\n"; graph<string> g2 = impossible_demo(); cout << g2.euler_path() << '\n'; return 0; } ~~~~ |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 05:01AM +0100 It just occurred to me that an `if(false) STM` does not always make the complete STM dead code: #include <iostream> using namespace std; auto main() -> int { int const n = 7; switch( n ) { case 1: cout << "one!\n"; break; if( false ) { case 7: cout << "seven!\n"; break; } default: cout << "Switch default.\n"; } } It also occurred to me that I don't know that standard's rules that make this case work as unexpected, but makes the code invalid when the `if` is replaced with a `while` or `switch`. Hm. Cheers!, - Alf |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 06:42AM +0100 On 25.02.2017 05:37, Stefan Ram wrote: > A nested »switch«, however, clearly starts a new scope > for case-lables, so it is correct, when »seven!« is not > printed in this case. Oh, it seems you're right. It looks like a compiler bug. With g++ replacing `if` with `while` makes the "seven!" output disappear: [example] [C:\my\temp] > type undead.cpp #include <iostream> using namespace std; auto main() -> int { int const n = 7; switch( n ) { case 1: cout << "one!\n"; break; while( false ) { case 7: cout << "seven!\n"; break; } default: cout << "Switch default.\n"; } } [C:\my\temp] > (g++ --version | find "++") && g++ undead.cpp && a g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0 Switch default. [C:\my\temp] > _ [/example] With Visual C++ 2015 it's executed: [example] [C:\my\temp] > (cl /nologo- 2>&1 | find "++") && cl undead.cpp /Feb && b Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86 undead.cpp seven! Switch default. [C:\my\temp] > _ [/example] Still, with the compilers disagreeing it would be nice to know what the standard says about this. I guess C++ /should/ behave like old C here for backward compatibility, e.g. in order to make the old Duff's device trick work. So it seems Visual C++ is right and g++ is buggy, but... :) Cheers!, - Alf |
Robert Wessel <robertwessel2@yahoo.com>: Feb 24 11:41PM -0600 On Fri, 24 Feb 2017 12:50:21 -0800 (PST), 嘱 Tiib <ootiib@hot.ee> wrote: >release nor 2011.1 extended feature releases of it. It is done. >Defects of 2011 were attempted to address in version 2014. That is >also final. Version 2017 is under development. Well, they do accept defect reports, and then issue corrections or whatever to the standard as is appropriate. Here are some of the ones for the C++11 core language: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html |
ram@zedat.fu-berlin.de (Stefan Ram): Feb 25 02:03AM >I confirmed, by writing my own little program for that, that you get a >correct result. Although it is for index 999.999 in the sequence, when >one considers fib(0) = 1, fib(1) = 1, fib(2) = 2, fib(3) = 3 and so on. I tried to align my numbering with the sequence oeis.org/A000045/b000045.txt which starts: 0 0 1 1 2 1 3 2 4 3 5 5 6 8 and I also used this for my test code: switch( i ) { case 2: assert( result == "1"s ); break; /* about 1997 lines omitted in this post */ case 2000: assert( result == "4224696333392304878706725602341482782579852840250681098010280137314308584370130707224123599639141511088446087538909603607640194711643596029271983312598737326253555802606991585915229492453904998722256795316982874482472992263901833716778060607011615497886719879858311468870876264597369086722884023654422295243347964480139515349562972087652656069529806499841977448720155612802665404554171717881930324025204312082516817125"s ); break; } >I'm not sure how your code works, it seems to do strange >things It was a hasty translation from my JavaScript program, but it uses some strange things so as to be fast. >here's the expressive C++ dialect code Usually I am the one who gets the flames for posting "unreadable code", but now you are heroically diverting the flames towards yourself! >I did not take the trouble to split the addition loop into parts where >the processing could be specialized for speed; I just used `-O3`. ;-) I have edited your source code very slightly (see below), and measured its execution time to be about 8 times the execution time of my program. OTOH your code is much more compact than mine. Your code, slightly modified: #include <algorithm> #include <iostream> #include <cmath> #include <vector> using bigint = std::vector<int>; static void addto( bigint & a, bigint const & b ) { auto size_a = a.size(); auto size_b = b.size(); auto carry = 0; for( decltype(size_a) i = 0; i < ::std::max( size_a, size_b ); ++i ) { if( i >= size_a )a.resize( i + 1 ); a[ i ]+= carry +( i < size_b ? b[ i ]: 0 ); carry = a[ i ]/10; a[ i ] -= 10 * carry; } if( carry )a.push_back( carry ); } int main() { auto const golden_ratio = 1.6180339887498948482045868343656381177203; auto const golden_log = ::std::log10( golden_ratio ); auto const n = 100'000; auto const n_digits = static_cast< bigint::size_type > ( ( n + 10. )* golden_log + 1 ); ::std::clog << n_digits << " digits expected.\n"; auto a = bigint{ 1 }; a.reserve( n_digits ); auto b = bigint{ 0 }; b.reserve( n_digits ); for( decltype(+n) i = 0; i < n; ++i ) { auto next = a; addto( next, b ); a = ::std::move( b ); b = ::std::move( next ); } for( auto i = b.size(); i > 0; )::std::cout << b[ --i ]; ::std::cout << '\n'; } |
ram@zedat.fu-berlin.de (Stefan Ram): Feb 25 04:07AM >N_TIMES( n ) Maybe a symbolic interpretation helps to understand this: N_TIMES( m ) N_TIMES_AUX( MM_CONCAT( i_, __COUNTER__ ), MM_CONCAT( i_, __COUNTER__ ), m ) N_TIMES_AUX( MM_CONCAT_( i_, 0 ), MM_CONCAT_( i_, 1 ), m ) N_TIMES_AUX( MM_CONCAT__( i_, 0 ), MM_CONCAT__( i_, 1 ), m ) N_TIMES_AUX( i_ ## 0, i_ ## 1, m ) N_TIMES_AUX( i_0, i_1, m ) N_TIMES_AUX( i_0, i_1, m ) N_TIMES_AUX( i_0, i_1, m ) Index i_0 = 0, i_1 = m; i_0 < i_1; ++i_0 Now what would happen if we would remove one MM_CONCAT level? #define MM_CONCAT_( a, b ) a ## b #define MM_CONCAT( a, b ) MM_CONCAT_( a, b ) N_TIMES( m ) N_TIMES_AUX( MM_CONCAT( i_, __COUNTER__ ), MM_CONCAT( i_, __COUNTER__ ), m ) N_TIMES_AUX( MM_CONCAT_( i_, 0 ), MM_CONCAT_( i_, 1 ), m ) N_TIMES_AUX( i_ ## 0, i_ ## 1, m ) N_TIMES_AUX( i_0, i_1, m ) N_TIMES_AUX( i_0, i_1, m ) Index i_0 = 0, i_1 = m; i_0 < i_1; ++i_0 Same result! Let's remove another level! #define MM_CONCAT( a, b ) a ## b N_TIMES( m ) N_TIMES_AUX( MM_CONCAT( i_, __COUNTER__ ), MM_CONCAT( i_, __COUNTER__ ), m ) N_TIMES_AUX( i_ ## __COUNTER__, i_ ## __COUNTER__, m ) N_TIMES_AUX( i___COUNTER__, i___COUNTER__, m ) Index i___COUNTER__ = 0, i___COUNTER__ = m; i___COUNTER__ < i___COUNTER__; ++i___COUNTER__ That would be too much. So you can remove one level of MM_CONCAT, but not more, i.e., the following still works: #define MM_CONCAT_( a, b ) a ## b #define MM_CONCAT( a, b ) MM_CONCAT_( a, b ) #define N_TIMES_AUX( i, n, expr ) Index i = 0, n = expr; i < n; ++i #define N_TIMES( n ) N_TIMES_AUX( MM_CONCAT( i_, __COUNTER__ ), MM_CONCAT( i_, __COUNTER__ ), n ) Disclaimer: I, too, do /not/ know the actual C++ standard rules, I just inferred to above reduction steps from the behavior of a specific implementation and some guesswork. |
ram@zedat.fu-berlin.de (Stefan Ram): Feb 25 04:37AM >It also occurred to me that I don't know that standard's rules that make >this case work as unexpected, but makes the code invalid when the `if` >is replaced with a `while` or `switch`. By »invalid«, you might mean that »seven!« will /not/ be printed in the case of a »while«. I believe that the standard requires the »while« to behave like the »if«, and when »seven!« is not printed, it's an error in the C++ implementation. A nested »switch«, however, clearly starts a new scope for case-lables, so it is correct, when »seven!« is not printed in this case. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 03:22AM +0100 My original N_TIMES macro, called $n_times, used a range based `for` loop. But g++ warned about the loop control variable being unused, and I found no way to shut it up in code (only way was to turn off that warning in the g++ invocation). So I'm thinking of using this scheme: #include <iostream> #include <stddef.h> using namespace std; using Size = ptrdiff_t; using Index = Size; #define MM_CONCAT__( a, b ) a ## b #define MM_CONCAT_( a, b ) MM_CONCAT__( a, b ) #define MM_CONCAT( a, b ) MM_CONCAT_( a, b ) #define N_TIMES_AUX( i, n, expr ) Index i = 0, n = expr; i < n; ++i #define N_TIMES( n ) N_TIMES_AUX( MM_CONCAT( i_, __COUNTER__ ), MM_CONCAT( i_, __COUNTER__ ), n ) auto main() -> int { int const n = 7; for( N_TIMES( n ) ) { cout << "! "; } cout << endl; } Here __COUNTER__ is a language extension supported by Visual C++, g++ and clang, and probably far more compilers. Along with the other language extensions I use (#pragma once, #pragma push, #pragma pop, $ in identifiers) it's widely supported but not standard, while all sorts of never-seen-in-the-wild things are being standardized. Hrmf. Innovating committee. I aim just for the supporting compilers. Anyway, even though I just wrote this code, and e.g. applied the concatenation trick out of some acquired instinct for that, I don't understand the details of how it avoids the actual macro argument `n`, given in `main`, being replaced with the value of the formal macro argument `n` i N_TIMES_AUX. It works but I don't know how... So, my attempt at breaking it failed, which I'm happy about. Also, that I identified a big hole in my understanding, that I could ask about. But is there still some way to break this code? Cheers! - Alf |
Steve Keller <keller@no.invalid>: Feb 25 01:10AM +0100 > return arg + 1; // ambiguity! is this the local parameter > // or the arg() function from <complex.h>? > } Yes, of course. But since in my code the call to exec(s) with Stmt *s doesn't match the definition of FooStmt::exec() I wouldn't count this as a match and continue matching in a wider scope and would find the actually matching global exec(const Stmt *). > s.exec(); > } > }; Hm, this looks like an interesting solution. I'll consider this. But for consistency I'd have to do it for class Expr {} with Expr::eval() also, although I don't have the same problem there. What I dislike a little is that in s.exec() the member s doesn't really look like a pointer anymore. Steve |
Steve Keller <keller@no.invalid>: Feb 25 01:10AM +0100 > [snip] > This design looks very strange to me. Why do you both inherit from > Stmt and include a pointer to Stmt? Actually, there is no FooStmt but WhileStmt, IfStmt. ForStmt, etc. which *are* statements and some contain other statements. There's also an abstract base class Expr with a number of subclasses, and my statements look like this class WhileStmt : public Stmt { Expr *cond; Stmt *body; public: ... }; class IfStmt : public Stmt { Expr *cond; Stmt *s1, *s2; public: ... }; class ForStmt : public Stmt { Expr *init, *cond, *iter; Stmt *body; public: ... }; class ExprStmt : public Stmt { Expr *expr; public: ... }; I think that is quite straight forward and natural. Steve |
Paavo Helde <myfirstname@osa.pri.ee>: Feb 25 03:25AM +0200 On 25.02.2017 2:10, Steve Keller wrote: > doesn't match the definition of FooStmt::exec() I wouldn't count this > as a match and continue matching in a wider scope and would find the > actually matching global exec(const Stmt *). Yes, this might seem like a good idea, but over the years I have learned the hard way that trying to be too clever is about the worst thing a language can do. The reason is that if the language is too clever then the programmer is not able any more to figure out how it works and thus it loses control over how to achieve that it wants. C++ already has function overloading, Koenig lookup and other things where it has arguably gone too clever, there is no need to make it even more "cleverer". > also, although I don't have the same problem there. What I dislike a > little is that in s.exec() the member s doesn't really look like a > pointer anymore. You can make it look like s->exec() with some more effort, though by some reason I suspect you are wasting your pedantry in wrong areas. There are no abstract ideals to which your code should conform. Instead, programming is an engineering discipline, meaning everything serves a purpose. Consistency of the code and markup is needed only for the maintainer programmer (including yourself 6 months ahead) to understand the program so he can easily maintain or refactor it. In this sense, writing some decent unit tests is probably much more helpful than a minor stylistic consistency. |
"Öö Tiib" <ootiib@hot.ee>: Feb 24 04:01PM -0800 On Saturday, 25 February 2017 01:30:23 UTC+2, Alf P. Steinbach wrote: > threads, it would be impractical to lock down the object any time its > `operator[]` returns a reference that possibly could be stored by the > thread and used to modify the string at arbitrary times. Thread safety is not orthogonal when we assume that particular data can be accessed only by one thread. That is "embarrassingly parallel". For example if a task needs string then it is copied into input data of task and then the task can be given to other thread to solve. However if copy was lazy (with CoW) then we now either have race conditions of naive CoW or inefficiency of non-naive CoW. > fine, I think. It's the responsibility of the code that uses those > classes, to ensure thread safe operation. E.g. by simply not sharing > mutable objects between threads. We can't if the CoW is done behind scenes. Then that means locks at wrong level of granularity inside of string AND locks outside as well if we really plan to use same string from multiple threads concurrently. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 01:38AM +0100 On 25.02.2017 01:01, Öö Tiib wrote: > task and then the task can be given to other thread to solve. > However if copy was lazy (with CoW) then we now either have race conditions > of naive CoW or inefficiency of non-naive CoW. To be clear, the situation you envision is that in thread B there is const Cow_string sb = ... initialized as a copy of tread A's Cow_string sa = ... Then when thread A modifies its `sa` it will have to stop the shared buffer ownership, which modifies some reference counter or whatever that's shared with `sb`, and might be concurrently accessed by B. But this is only problem with an incorrect COW implementation, because the cost of ensuring atomicity of the reference counting is miniscule, completely insignificant, compared to the cost of copying the data, which is the other part of that un-sharing-of-buffer operation. > We can't if the CoW is done behind scenes. Then that means locks at > wrong level of granularity inside of string AND locks outside as well > if we really plan to use same string from multiple threads concurrently. I think we can. Yes it involves atomic operations or locking for the reference counting. That's IMHO at the right level. It doesn't make the class generally thread safe, which I see as an impractical goal (and on the way to discovering how impractical, introducing awesome inefficiency), but it makes it just thread safe enough to allow simple logical copying over to another thread, without having to explicitly clone the object or check its reference count – which is of course another option, to just be completely explicit instead of relying on implicit, but then in a very non-ideal way "leaking implementation details". Cheers!, - Alf (expressive, so far without gibberish diagnostics, yay!) |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 25 12:30AM +0100 On 25.02.2017 00:24, Paul wrote: > I noticed this: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms > However, it seems to be exhaustive, without stressing the more common > and standard patterns. I haven't clicked it but "More" seems to imply a separate original list. Cheers & hth., - Alf |
Paul <pepstein5@gmail.com>: Feb 24 03:52PM -0800 On Friday, February 24, 2017 at 11:31:54 PM UTC, Alf P. Steinbach wrote: > > However, it seems to be exhaustive, without stressing the more common > > and standard patterns. > I haven't clicked it but "More" seems to imply a separate original list. It contains the basics like copy-and-swap, RAII etc. but it also contains obscurities like Barton Nackman trick. "More C++ idioms" is an abbreviation for "More C++ idioms than you would think would be contained in a book on C++ idioms." In fact, the above is what the book used to be called, but the title was thought to be insufficiently concise. Paul |
"Öö Tiib" <ootiib@hot.ee>: Feb 24 04:14PM -0800 On Saturday, 25 February 2017 01:52:38 UTC+2, Paul wrote: > think would be contained in a book on C++ idioms." > In fact, the above is what the book used to be called, but the title was > thought to be insufficiently concise. If you plan to work in a team with other people and on real code base then you may use yourself some subset of idioms but you can't avoid dealing with the ones that you don't use. |
"Öö Tiib" <ootiib@hot.ee>: Feb 24 03:39PM -0800 On Friday, 24 February 2017 22:08:29 UTC+2, Tim Rentsch wrote: > stuff. To look at any code more than about 10 or 20 > lines I would like to be able to compile it. If the > code is written in "expressive" C++ I can't do that. +1 If we need compiling language with "var" and "let" and "func" then we can take rust or swift; there are no need to mutate C++ into one of those. Attempts to mutate C++ into different language by using preprocessor and template meta-programming tricks are not too good. I have tried to use such things but all immersion went immediately away when there was some typo in my code. The compiler typically did not point at the typo of mine but spit several rather incomprehensible pages of gibberish about something being wrong with internals of those meta-programming tricks. That just confused, wasted time and improved nothing. |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment