Jump to content

David Schwartz

Members
  • Content Count

    1237
  • Joined

  • Last visited

  • Days Won

    25

Everything posted by David Schwartz

  1. In another thread, I was asking about a situation that highlights a very poor design choice by Delphi's DB team (this goes back to D2). They created TDataset, which has a rich set of stuff that's quite adequate for most purposes. But then they created TQuery that IS-A TDataset. Unfortunately, they specialized it to the point where it's impossible to serve as a base class for other types of queries. I'm working with two 3rd-party libs that support Oracle and Postgres. They both have a Query component derived from TDataset instead of TQuery. So while both provide the same "logical" support for SQL queries, they're not quite the same in how they do it. I'm trying to create a single common procedure I can use for both of them, but it's nearly impossible without adding a flag somewhere that says which one you're using, then doing if...then...else to select the correct approach depending which type it is. This is exactly what polymorphism is supposed to address. But because the TQuery subclass is too narrowly defined, it's impossible to do. Instead, you have to inherit from something more abstract, TDataset, which leads to a mess. TQuery can neither be used as a base class, nor re-used for similar purposes. I'm guessing that the original designers could only imagine a huge adapter like BDE being used to handle different database query types, and this general TQuery object would delegate DB-specific activities to that adapter. History has shown that to be a poor decision, and no effort has been made to create a better TQuery-like subclass from TDataset that could serve as a base for other types of query objects.
  2. hmmmm... actually it's a bit more complex that I first realized ... There's the SQL property, which is a TStrings -- that's common to both query classes. But it turns out they have different ways of supporting SQL parameters. One requires you to define each parameter and its type, and the types are not the same "stuff"; in the other, they're optional. One can do auto-prepare, the other requires an explicit call to Prepare, but both can take an explicit call to Prepare. -- Both can do auto-Prepare when the .SQL.Lines property is assigned. One uses qry.ParamByName(...).AsSomeType := blahblah to set values, the other uses SetValue or something like that I'm not sure if even IProviderSupport covers these inconsistencies. Boy ... what a mess.
  3. Sorry, I missed this earlier. Same as what I told Rudy ... these are two different libs from separate 3rd-party vendors. Otherwise, it's a fine idea. 🙂
  4. @Rudy Velthuis No, I didn't write these. They're two different 3rd-party libs.
  5. In theory it's great, as long as you're building the entire library. Unfortunately, Delphi didn't make it easy to do in this case. TDatasets and TQuerys already exit. The 3rd-party lib devs inherited from TDataset and added their own query support, so they're peers of TQuery, not descendants of it. We don't have the option of going back and redesigning TQuery, along with its relationship to TDataset. It's also instructive to note that TQuery specializes TDataset; it hides as much as it adds, making it unattractive to use as a parent class.
  6. I suppose there's a huge gap between theory and practice. If you look at the biggest, most mature libraries around, they're all flat. Any inheritance is pretty much within their own frameworks. DOS and Windows started out with APIs as software interrupts, then TSRs, then DLLs. The introduction of C++ only offered lightweight wrappers for most of their smallest objects, and didn't go very far in terms of building a hierarchy of anything. Several GUI libs were introduced, then the VCL came along that was rooted in a TObject, which at the time was seen as "revolutionary" and even somewhat controversial. Indeed, even TurboPascal adherents argued it created confusion with their 'object' type, although it was part of a library rather than the language. Most Delphi libs now derive from some fairly low-level classes, but all are at least TObjects. However, trying to build object-oriented libs around flat DLLs is challenging, to say the least. I've tried it with a couple of Google's APIs and it's a huge challenge with a strongly-typed language like Delphi; maybe a lot less for javascript or more loosely-typed languages. I find I'm extremely hesitant to derive specialized classes from things in 3rd-party libs, unless there's absolutely no question that they won't be useful anywhere else. That's often the case for GUI objects, but not for non-visible functional things. In the latter case, Interfaces are more helpful because you can replace the logic without having to rewrite anything (assuming the model your Interface defines is properly designed, which isn't always the case). Otherwise, composition is the way to go. (Actually, sometimes I'll encapsulate the lib inside of a class to provide a layer of isolation between the lib and code that uses it. This is a mix between composition and inheritance based on interfaces.)
  7. @Lars Fosdal that's great! I'm chuckling because you're using the TxQueryClass as the discriminant. I had not thought of that. Otherwise it's very straightforward. Sweet!
  8. well, wouldn't you still need something to say which one you need? I was thinking of a regular Pascal Variant Record, which would work fine to hold both types of pointers. But you'd need a flag to say which one to pick. Unless ... in the case of a wrapper class, you just set one instance (pointer) and internally the logic selected the one that's not NIL. Or am I missing something?
  9. very cool! I never knew about this. 🙂 EDIT: But this interface says it's deprecated. Has it been replaced with something else?
  10. David Schwartz

    where are search paths stored?

    Where are search paths stored? There are the global search paths in Tools | Options (set when you install components), and the local ones that can be added for individual projects. I'm running into an issue where I pulled a project out of git into its own isolated folder tree and tried to build it. I'm getting compiler errors because there are hits on previously unknown conflicts in component libs I have in my system. Clearly there are some ordering differences in search paths. I compared .dproj files and didn't find any search paths that contained the offending folders (either implicitly or explicitly). Maybe a better question is ... how do you go about setting up projects so everybody can extract the source code from git and build each project, and they result in substantially equivalent EXEs? Right now I can't get this one project to even compile due to component library conflicts even though we supposedly have the same component libraries installed. (In this particular instance, we've got an old library from D7 era that was brought forward into DX10.2 and cleaned up so it doesn't produce the tons of hints and warnings it used to. In attempting this build, I discovered that a 3rd-party component vendor has incorporated this exact same library into their own source tree, and the compiler is finding this copy rather than the older one that's isolated and installed as a component. This newly discovered code is the older version with all of the hints and warnings from D7 days -- it was never cleaned up for newer Delphi versions. It also has some errors that stop the build. I'm trying to figure out how this folder is even being included in the library search path, as it's not in MY global search paths that Delphi loads up.)
  11. David Schwartz

    where are search paths stored?

    So they're not stored anywhere that would be captured by a source code control system like git, eh? hmmmm..... How in the world would this particular path be "unmasked" from another project if the lib search paths are not affected? (And if it's not from a uses clause containing an absolute path to it somewhere...)
  12. David Schwartz

    Solution(s) for coder with quite poor eyesight

    FWIW, a 55" 4k monitor is equivalent to a 2x2 layout of 27" monitors without the seams between them. Windows 10 has a setting somewhere where you can enlarge windows and it will stretch everything inside it to make their contents proportionally larger. I don't know what that setting is, but I've seen it in action a few times when it was enabled accidentally. I enlarged my Delphi IDE once and it just magnified everything as I made the window bigger. That's not what I was trying to do, and it took a while to disable that setting. I don't even remember what it was.
  13. This can be done without locks, but it takes two flags, an atomic test-and-set operation (or increment-and-return-value), as well as a strict protocol that must be followed by everybody. I don't see evidence of that here. As an aside, what options are there in Delphi for an atomic test-and-set or incr-and-return-value operation? I'm not sure anything at Delphi's source language level could be considered "atomic" for concurrency purposes. TRIVIA FACTOID: There was never a real-time or multi-tasking version of any OS built that ran in "protected mode" on Intel's 80286 chip, because the microinstructions that implemented the PUSH IP operation in protected mode could be interrupted between incrementing SP and setting [SP] = IP.
  14. Well, I probably chose the worst possible example to use in Delphi, and TBH, I didn't really think about it. As long as you stick with the default 'string' type, you're pretty safe ... most of the time. That's what I do, mainly so I don't have to think about it. For someone learning the basics of OOP, strings and integers are obvious types to use for examples. I'm certainly not going to defend the presence of an intolerable plethora of string types where all sorts of things have been added to the language and RTL to try to smooth their incompatibilities. If you guys don't like my examples, why don't you offer up better examples rather than picking my examples apart? I personally don't care, but the guy who's trying to figure this stuff out is probably watching with his jaw on the desk as a bunch of experts are trying to one-up each other showing off their knowledge of arguably obscure language features instead of helping a newbie understand basic concepts. Y'all are brilliant when it comes to uncovering the source of problems rooted in really obscure issues, a skill I really appreciate being able to access from time to time. But that's not what this thread is about. So I'm going to shut up now and let somebody else jump in ... if there's anything left of this poor fellow's desire to learn some basic OOP principles. 🙂
  15. This guy is asking about basic concepts of encapsulation and you guys are going off the rails trying to explain the historical evolution of the bit arrangements of strings from early 1990's through today in TurboPascal -> 16-bit Delphi -> 32-bit Delphi -> Unicode Delphi, and using that to imply that the entire concept of encapsulation is bogus and can never be trusted.... never mind the fact that this 2019 and newbies are probably only using the latest (Unicode) versions of Delphi (and certainly not some old version of TP or 16-bit Delphi, but maybe a pre-Unicode version of Delphi). So when you teach OOP to beginners now, do you say it's only about inheritance and polymorphism ... before you proceed to shred those concepts as well? Or do you tell people not to use properties or Interfaces because technically speaking they cannot be trusted to last for 30 years since they don't anticipate language and platform changes accurately?
  16. I think what DH is saying is that there's nothing that prevents the author of the GetABC function from returning fxyz or FloatToStr(sin(2*pi*r*r)) or just a random string. That's true. Reminds me of a time way back when color monitors were $4,000 and I was advocating for the use of colors to help highlight errors in red, success in green, and stuff like that. A contrarian guy on the team argued, "How do you know that someone won't display errors in green and successful results in red?" Indeed, I'm working on a project now where we're porting an app from Oracle to PostgreSQL, and there are functions that are not returning what we're expecting. It's a major headache to track this stuff down. Assuming there are some reasonable conventions being followed (which you should always verify if at all possible), then a function purporting to return a string version of a certain variable can be counted on to do that, while a function that returns an integer version will do that instead -- regardless of how that variable is implemented. So if it's defined as a string, then the version of GetABC : string just returns fabc while the integer version returns StrToInt(fabc) -- and you need to ensure that fabc is supposed to be an integer and not a float. The point is, the function acts as an adapter to convert whatever the implementation type is into whatever type the function returns. And if you change the implementation type, then code that accesses it directly will either fail to compile or fail at run-time, while the code that uses the adapter function will continue to work (assuming you update the function properly). The function hides the implementation. Properties are just sugar coating that make accessors & mutators easier to use.
  17. your explanation is even implementation-dependent! 🙂 I think you're missing the point. It's not about the means used to access something. If you have a variable "fabc : integer" and somewhere you want to display it as a text value, fabc.ToString depends on you KNOWING that fabc IS declared as an Integer, right? That's what meant by "implementation details". There's no method being used here! The GOAL is a solution where you do NOT KNOW HOW IT'S IMPLEMENTED. Is it an integer? A string? A float? A date? A means of querying a database? Something that sends a request to a SOAP or REST API to get a result back? The point is, when you need that value in whatever format, you get it in that format, without having to KNOW how it's actually implemented. There are some terms, "getter / setter" or "accessor / mutator" that refer to methods used to get and set values without regard to how they're implemented. In Design Pattern terms, I believe these are called Adapters. Meaning, you use an Adapter pattern to adapt one thing to another. No matter HOW fabc is defined within the class, you can be sure that a method like function GetABC : string; will ALWAYS return the contents of fabc as a string, regardless of how fabc is actually declared. And regardless of whether fabc is private, protected, public, or published. This also goes under the more generic topic of "encapsulation" regarding object-oriented programming.
  18. David Schwartz

    CreateProcess[A] only returns Chinese

    I've got this piece of code from years ago called TRedirectedConsole that's a class wrapping a call to CreateProcess. It uses pipes to grab StdOut and StdErr, and it's mainly to let you run an EXE and capture the output in an event handler that lets you write it to something like a TMemo. I have not used it in ages, and the timestamp is dated 2004. I dug it out of my archives and it compiles just fine in Delphi 10.2.3 after a minor fix. But when I try to use it, all I can get it to output is gibberish that looks mostly like Chinese characters (could be some other Asian language for all I know). Does anybody have an experience making CreateProcess[A] work properly in Delphi? RedCon.zip
  19. David Schwartz

    CreateProcess[A] only returns Chinese

    I'm getting a compiler error from this: [dcc32 Error] RedCon.pas(174): E2250 There is no overloaded version of 'UnicodeFromLocaleChars' that can be called with these arguments [dcc32 Error] RedCon.pas(175): E2250 There is no overloaded version of 'UnicodeFromLocaleChars' that can be called with these arguments The problem seems to be that the function wants PAnsiChar for Buf, which is an array of AnsiChar. If I try casting it to PAnsiChar, I get another error saying it's an illegal cast. EDIT: I changed it to @Buf[1] and that works. AND ... it works great now. Thanks!
  20. David Schwartz

    CreateProcess[A] only returns Chinese

    Thanks, that's great. But it's also why I was puzzled to see the mojibake, because I assumed the data would be converted automatically to Unicode. I guess the trick is knowing exactly where in the pipeline the conversion needs to occur (if/when it does) and making it happen.
  21. David Schwartz

    CreateProcess[A] only returns Chinese

    Ok, one more question. What's the difference between using CreateProcess, CreateProcessA, and CreateProcessW if the internal process being spawned may or may not be producing data in the expected format? (Ansi vs. Unicode) Why would you choose one or the other if it depends more on the ReadFile() ?
  22. David Schwartz

    CreateProcess[A] only returns Chinese

    Remy, this is an awesome explanation. To summarize: there's an impedance mismatch of sorts between the command being run and the data it's spitting out vs. the rest of the processing pipeline. The command I was testing is the simple "dir c:\" command. It's running on Windows 7, so I'm guessing that everything outside this process is all Unicode. Why is a mismatch arising? Just out of curiosity, what's the dominating factor here? The app (command.exe); the program (dir c:\); the ReadFile() function; or the type of the storage array used? Or does everything have to be in alignment / agreement? (I did study the code before posting here, and it is a bit mind-numbing in this case.) What's not quite fully distilled for me is this; it sounds like the "data pipeline" around the CreateProcess call (including all of the various handlers) may need to be tailored for EITHER Ansi OR Unicode. Is this true? Or is there a way to handle both? I guess it's irrelevant tho, because I plan to use this to run exactly one program (Ora2Pg, which is built on top of a Perl platform). That said, what's the simplest way to deal with this issue?
  23. David Schwartz

    libpq.dll for FireDAC?

    I cannot seem to find a libpq.dll for Postgres that works with FireDAC in D10.2.3. Any suggestions on where to get a copy? NOTE: Please don't say to ask Google! I've pulled down five different versions that all have different datetime stamps and are supposedly 32-bit versions, but none of them work! If you have one that DOES work, where can I get a copy?
  24. David Schwartz

    How to replace whole words in a string

    I use the RegExpr library from years back. It's great. Very easy to set up patterns to detect words.
  25. There used to be a library named Hipparchus that used Voronoi maps to accomplish this sort of thing. Microsoft bought Hipparchus, converted it to C# (from Fortran and C) and embedded it into SQL Server as it's GIS engine. These guys published an article in Dr. Dobb's back in 1992 or so that explained it very well. They also published c code and some tools in conjunction with that article. But they also sold the Hipparchus library commercially. It was a lot more involved than what the Dr. Dobb's article did. Since MS bought them out, they started an open-source effort to do similar stuff, but I haven't seen any updates in quite a while. I'd love to get hold of a copy of their library with the source code, if anybody might know of a copy sitting on a shelf somewhere. Unfortunately, once they sold to MS, the company had to pull their stuff off the market. I learned about the sale about a month too late. 😞 Essentially, there's one app that preprocesses a sampling of the data and creates a voronoi map. Then all of the points are translated from lat/lon to vectors based on the centroid of a voronoi cell where each point is a tuple <cos(p), offset>. As it happens, all of the math can then be done with simple 16-bit signed integer calculations, which makes looking for common types of things, like nearest-neighbor clusters in a set of points, extremely quick. But, if you have access to MS SQL Server since 2010 or so, I believe this logic is included in their GIS package.
×