Jump to content

David Schwartz

Members
  • Content Count

    1191
  • Joined

  • Last visited

  • Days Won

    24

Everything posted by David Schwartz

  1. David Schwartz

    Tool to convert form components to run-time and vice-versa

    I'm aware of GExperts, but I thought it was something else.
  2. It wasn't my choice, and I personally don't care. It's just what I've been given to work with. Upper Management makes all sorts of decisions regardless of my input, for reasons that are opaque to me, and I've learned it's safer to just accept their decisions.
  3. Your example highlights the fallacy of your assertion. In fact, inheritance locks you into whatever resources are required by the parent class! In this case, Indy. FTP is a very generic thing. Why inherit from a specific concrete implementation that locks you into it's view of the world for ever and ever? I think a far better approach would be to "inherit" from an abstract base class (a.k.a. "interface") that would allow the use of virtually ANYTHING that supports generic FTP features, including the one you happen to prefer today. But at that point, you're about 50/50 whether that approach is better than using composition (HAS-A) to include an API that offers the same abstract interface, possibly as a drop-on-the-form component. Unless your goal is creating a bigger, better, faster, or more specialized version of the base class, then inheritance of a non-abstract base is silly. Without multiple inheritance, you're locked into specializing or expanding a single concrete base class anyway. So if you're building a class that EMPLOYS FTP, for example, but IS NOT INTENDED to BE a "better" FTP service, then inheritance is clearly a very poor choice. And inheriting from an abstract class presented as an Interface is simply a way of mixing-in a single type's namespace with your component's namespace. It's effectively just "anonymous composition" because you simply refer to the methods and properties as if they're part of your class, without having to refer to the name of the object containing them. Finally, you can do dependency injection just as easily without interfaces.
  4. Mida is a tool that was written to simplify translating VCL apps to run under Firemonkey. It evolved to the point where V5.x Studio edition started supporting arbitrary rewriting of components without requiring VCL->FMX. I'm looking at using it to translate a bunch of VCL apps that use Allround Automation's DOA Oracle components to use PgDAC components instead. It looks like it should do the trick, but it requires a file in INI format that isn't clearly documented. They have some examples for BDE, FD, and a couple others. But I can't find any detailed explanation of how to go about creating the .mida file for something else, or even how to set one up. Are they built entirely by hand? Or does the Studio version let you build them somehow? They're a little slow on responding to support requests; the videos on YouTube haven't been updated since V2 was released; and I can't find any help files or documentation anywhere. (I have a licensed version of V5.6 Studio.) So I thought I'd ask here and see if anybody has any experience they can share.
  5. perhaps. But we've got to migrate to one or the other, regardless. Also, FD uses a DLL for Postgres, while PgDAC goes direct.
  6. 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.
  7. 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.
  8. 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. 🙂
  9. @Rudy Velthuis No, I didn't write these. They're two different 3rd-party libs.
  10. 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.
  11. 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.)
  12. @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!
  13. 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?
  14. very cool! I never knew about this. 🙂 EDIT: But this interface says it's deprecated. Has it been replaced with something else?
  15. 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.)
  16. 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...)
  17. 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.
  18. 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.
  19. 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. 🙂
  20. 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?
  21. 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.
  22. 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.
  23. 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
  24. 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!
  25. 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.
×