When to IncludeFile and when to UseModule?

Just starting out? Need help? Post your questions and find answers here.
User avatar
SnowyDog
User
User
Posts: 21
Joined: Tue Jun 10, 2014 8:18 pm

When to IncludeFile and when to UseModule?

Post by SnowyDog »

Quick question regarding the choice of when to use IncludeFile rather than UseModule and vice-versa.

I have a rather large PureBasic application and wish to split the source code up to improve maintainability and also to re-use many of the functions in other applications. Additionally, I have some user-defined structures that are common to many of the functions in application Main() and sub-functions.

At first glance it would appear that grouping related Procedures together in separate Modules / namespaces is the way to go. This is certainly theoretically possible with my code. However I'm not sure whether this is the best way to go, since I don't really want to treat these modules as independent code blocks. I want to simply be able to call the individual functions from Main() and have them also available to functions located in the other "modules" that comprise the source code.

So, perhaps I should instead be using IncludeFile in the Main() source code to include each group of functions?

Some additional, related questions: -
1. When using the Module approach, is UseModule <name> always needed before calling a publicly-declared function or addressing a publicly-declared variable in that module, or can the namespace syntax be used for each statement referring to that function or variable?

2. When using either the Module or IncludeFile approach, where exactly should user-defined structure types that are common to multiple functions, be defined?

On this latter point, I've found that if I make a "structures.pb" file which contains the definitions for structures that are common to functions in different "modules" and then specify either IncludeFile or XIncludeFile into Main() and each of the separate modules, either one of the modules fails to "see" the structure types in that file or the compiler tells me that the structure has already been globally defined. Not sure how to overcome this.

Historically I have written Windows GUI / Console apps using PowerBASIC. With PowerBASIC, I have grouped associated functions, declared them as Public and have compiled each group to a Static-Linked Library (.SLL). These .SLL files are then simply linked into the source code that needs to reference them, and that approach works fine.

So, am interested to know a best practice approach for tackling this in PureBasic please?
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: When to IncludeFile and when to UseModule?

Post by spikey »

There's a module tutorial at: https://www.purebasic.fr/english/viewtopic.php?t=70686
SnowyDog wrote: Fri Jan 28, 2022 12:56 pmSo, perhaps I should instead be using IncludeFile in the Main() source code to include each group of functions?
It's not an either use 'IncludeFile' or use 'UseModule' situation - they both serve different purposes and can also be used in conjunction.
IncludeFile tells the compiler to insert the contents of the specified file at the current 'cursor' position. So this line includes UtilityFunctions.pbi in the current program:

Code: Select all

IncludeFile "SomePathTo\UtilityFunctions.pbi"
I've not used PowerBasic before but I think this is directly equivalent to the PowerBasic #INCLUDE metastatement and a pbi file is equivalent to an inc file.

UseModule tells the compiler to treat the members of the specified module as being in the current module so that you don't have to specify the other modules identifier in full. So, if the file UtilityFunctions.pbi contains a module called Utility I have to call its procedures something like:

Code: Select all

Utility::DoSomethingHelpful(anArgument)
Unless I previously specify it with UseModule, which saves some typing if you use it more than once:

Code: Select all

UseModule Utility
DoSomethingHelpful(anArgument)
However in this case there must be no name collisions anywhere between the current module and the utility module otherwise the compiler won't be able to resolve the ambiguity. This doesn't apply when you fully qualify names.

You can use either/both to accomplish what you want but modules are something slightly different...
SnowyDog wrote: Fri Jan 28, 2022 12:56 pmSo, perhaps I should instead be using IncludeFile in the Main() source code to include each group of functions?
That's the most direct solution to what you're trying to achieve, you just need to arrange things so that you don't try and include the utility code more than once and everything is declared in the right scope. You must have declared a 'thing' before you use the 'thing' in a function call. This applies to all 'things', procedures, structures, variables, interfaces...

This won't work:

Code: Select all

DoSomethingHelpful(anArgument)
IncludeFile "SomePathTo\UtilityFunctions.pbi" ; (which contains this but NOT in a module...)
Procedure DoSomethingHelpful(aParameter)
  ; Uses a global variable MyGlobal.
EndProcedure
; back in the parent file later on:
Global MyGlobal
This will work:

Code: Select all

Global MyGlobal
IncludeFile "SomePathTo\UtilityFunctions.pbi" 
DoSomethingHelpful(anArgument)
This won't work:

Code: Select all

IncludeFile "SomePathTo\UtilityFunctions.pbi" 
DoSomethingHelpful(anArgument)
; ...
IncludeFile "SomePathTo\UtilityFunctions.pbi" 
DoSomethingHelpful(anotherArgument)
This will:

Code: Select all

IncludeFile "SomePathTo\UtilityFunctions.pbi" 
DoSomethingHelpful(anArgument)
; ...
DoSomethingHelpful(anotherArgument)
(You could get around this second case by using XIncludeFile instead but really it's poorly structured code and would benefit overall from being organised so this isn't necessary.)
SnowyDog wrote: Fri Jan 28, 2022 12:56 pmI don't really want to treat these modules as independent code blocks.
I want to simply be able to call the individual functions from Main() and have them also available to functions located in the other "modules" that comprise the source code.
Modules are independent code blocks, you will have to adjust your work to them. The programmer must write in sharing facilities in some way when using modules - they won't just appear from nowhere but modules provide facilities to do this.

But to the best of my knowledge there is no program that can or cannot be created with or without using modules that couldn't be rewritten the other way if you really wanted to. However the work done and the structure of the code won't be the same in both methods, and converting from one to the other would be tedious and time consuming as there's no way to automate the process (not fully anyway).

Using modules may force the code to be structured in a different way. In order to take advantage of the Public/Private scopes you need to mix and match, make some declarations public, make others private.

You'll almost certainly need some kind of Common module with public declarations for most stuff to be shared between the other modules.
Using a module revokes access to the unnamed 'Not otherwise in a module' scope. You have to commit to modules entirely, which some see as a major disadvantage. It isn't really, it just requires you to work in a slightly different way and maybe requires some rethinking now and then.
SnowyDog wrote: Fri Jan 28, 2022 12:56 pm 1. When using the Module approach, is UseModule <name> always needed before calling a publicly-declared function or addressing a publicly-declared variable in that module, or can the namespace syntax be used for each statement referring to that function or variable?
No, UseModule isn't needed at all if you're happy writing fully qualified names all the time.
In the situation you describe it would probably make sense for you to have a module called Common:: say and put all your common stuff inside it; then put a UseModule Common inside all the individual modules so that the Common things can be used without the scope identifier each time. It stays in effect until you UnuseModule it.
SnowyDog wrote: Fri Jan 28, 2022 12:56 pm 2. When using either the Module or IncludeFile approach, where exactly should user-defined structure types that are common to multiple functions, be defined?
Without using modules it depends on your preference with the exception that an object must be declared before it is actually referenced as explained above. I tend to put all structure definitions together in one file because then I've only ever got one file to look in. This approach is slightly incongruous with Modules though and you'd need to do something slightly different. Other people put them in the file which uses them, which makes sense from another perspective but can get complicated if something belongs in more than one place.
You can use the Defined compiler directive to resolve this sort of thing, see https://www.purebasic.com/documentation ... tions.html

When using modules the best place would be a public definition in the Common module. In fact I'm not sure you could do it any other way.
SnowyDog wrote: Fri Jan 28, 2022 12:56 pm On this latter point, I've found that if I make a "structures.pb" file which contains the definitions for structures that are common to functions in different "modules" and then specify either IncludeFile or XIncludeFile into Main() and each of the separate modules, either one of the modules fails to "see" the structure types in that file or the compiler tells me that the structure has already been globally defined. Not sure how to overcome this.
This is more complicated and we'd need more information to give you a sensible answer but first off I'd guess you've got the sequence wrong or you've got the scope wrong or you're including the declarations more than once, or more than one of those. Recheck your code carefully there's probably something wrong somewhere.

Note though that it is possible to create an exclusivity problem when using modules which can only be fixed by repositioning something. This won't work even though the code appears to be syntactically correct:

Code: Select all

DeclareModule Common
  Define CommonVariable
  Declare CommonThing()
  Declare Results()
EndDeclareModule

Module Common
  Procedure CommonThing()
    Something::PublicVariable = 10
  EndProcedure
  
  Procedure Results()
    Debug Common::CommonVariable
    Debug Something::PublicVariable
  EndProcedure
EndModule

DeclareModule Something
  Define PublicVariable
  Declare DoThing()
EndDeclareModule  

Module Something
  Procedure DoThing()
    Common::CommonVariable = 20
  EndProcedure
EndModule

Common::CommonThing()
Something::DoThing()
Common::Results()
The only way to get it to work properly is to resequence so that the DeclareModule sections are directly adjacent and appear before the implementation sections; or rewrite it so that this is unnecessary.
Bitblazer
Enthusiast
Enthusiast
Posts: 733
Joined: Mon Apr 10, 2017 6:17 pm
Location: Germany
Contact:

Re: When to IncludeFile and when to UseModule?

Post by Bitblazer »

Don't forget that you also have DLLs and LIBs (and seperate executables as services even). I have started to rewrite old projects and replaced many includes with modules and new projects always are module based for me. I like modules because they help in project organisation and give Purebasic a bit better structure and isolation, like other languages have with classes. Previous to modules, i "had to" use a "silly" naming scheme in included sources, now the modules helped to achieve the namespace isolation without that. But i am still not fully happy with how i use them. It seems natural to have "some" common functions in all modules like "init, exit, cleanup" but that makes using the "usemodule" command often difficult.

And about the other mentioned problem. I do have a seperate common module for those resources that many applications and several modules themselves need. Kind of an application core framework - my personal STDIO lib module ;)
webpage - discord chat links -> purebasic GPT4All
User avatar
SnowyDog
User
User
Posts: 21
Joined: Tue Jun 10, 2014 8:18 pm

Re: When to IncludeFile and when to UseModule?

Post by SnowyDog »

Thanks Spikey for taking the time to respond with such a comprehensive reply. Also for the link to the tutorial. This has certainly given me a lot to think about in terms of structuring my PureBasic programs.

Looking back at my post I realise that I have confused the concept of modules (or classes) with separate source code files. Initially, I assumed that a module referred to a separate set of related functions in a separate source code file and that each of these in a project were compiled and then linked with the application Main(), which is what I am used to doing in PowerBASIC.

My preference is to have a separate source code in the project file per class / module, so that I can share each class with other applications. But I have to reset my thinking regarding modules in PureBasic. I'll do some experimenting.

Thanks also Bitblazer for your reply. I'm not familiar with LIBs in PureBasic so again I need to do some reading and playing!

Cheers!
Robin
User avatar
SnowyDog
User
User
Posts: 21
Joined: Tue Jun 10, 2014 8:18 pm

Re: When to IncludeFile and when to UseModule?

Post by SnowyDog »

Despite your very detailed comments and having spent further time playing with this, I'm still struggling with this concept on a practical basis and could do with your further help and advice, please.

My specific problem is regarding the order in which functions are defined / declared when using multiple modules or source code files in a project and the fact that this behaviour adversely affects the ability of one function to "see" another, in the scenario where functions need to call functions in other modules or source code files.

My preference for project layout is to either have modules or a separate source code file for each related "class" of functions. To make projects easy to maintain, I want to avoid having the same function defined more than once, although it needs to be called by functions in different source code files or modules.

Regardless of whether I use modules or have separate source code files for my classes of functions, the order in which the functions are Included within the main source code file always defines whether or not a function in one module / source code file can see another. I can't see how to make it so that either function is always aware of the other.

I note that the module tutorial and your comments suggest the use of a "common" module so that functions in different modules / source code files may be shared by others. But, I can't see how that works in practice. Is the "common" module not just the "main" source code file? What am I missing?

Hopefully my explanation makes it clear what I mean, but I show the following code as an example: -

"Main" source code file

Code: Select all

; PureBasic Sources as include files - Testing to see how they work
;
; Main()
;
EnableExplicit

IncludeFile "C:\PureBASIC\Projects\Development\Sources\Source2.pb"
IncludeFile "C:\PureBASIC\Projects\Development\Sources\Source1.pb"

Source1_Function()
Source2_Function()
"Source1" source code file

Code: Select all

; This is Source file 1
;
EnableExplicit

Declare Source1_Function()
Declare Source1_Function_That_Calls_Source2_Function()
   
Procedure Source1_Function()
  
  Debug "In Source1_Function()"
  ProcedureReturn 1
  
EndProcedure

Procedure Source1_Function_That_Calls_Source2_Function()
  
  Debug "Now in Source1_Function_That_Calls_Source2_Function"
  Source2_Function()  
  
EndProcedure
"Source2" source code file

Code: Select all

; This is Source file 2
;
EnableExplicit

Declare Source2_Function()
Declare Source2_Function_That_Calls_Source1_Function()
   
Procedure Source2_Function()
  
  Debug "In Source2_Function()"
  ProcedureReturn 1
  
EndProcedure

Procedure Source2_Function_That_Calls_Source1_Function()
  
  Debug "Now in Source2_Function_That_Calls_Source1_Function"
  Source1_Function()
  
EndProcedure
This project cannot be compiled because "Source2" fails to compile. It's included in the "Main" source code before "Source1" and therefore the function Source1_Function() has not been declared as far as Source2_Function_That_Calls_Source1_Function() is concerned.

How do I ensure that in this case, functions in both Source1 and Source2 are aware of and can call each other?

Thanks
Robin
User avatar
jacdelad
Addict
Addict
Posts: 1431
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: When to IncludeFile and when to UseModule?

Post by jacdelad »

Put the "Declares" into the Main-File, and just once.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
User avatar
SnowyDog
User
User
Posts: 21
Joined: Tue Jun 10, 2014 8:18 pm

Re: When to IncludeFile and when to UseModule?

Post by SnowyDog »

Yes that works for the include model. It doesn't really help in terms of project maintainability though since if a function's parameters are changed in one source file during development, I also need to change the declaration in the (separate) main file.

I guess if I go that route, I could have an include file containing all declares for every source file in the project, which at least means that the main code for different projects doesn't need to be updated each time. Some applications won't actually use all the functions in my "library" of code anyway.

What about when using modules? Isn't the purpose of using modules to produce a self-contained class of functions, so a function and its declaration are completely contained in one module / source code file? Although you can use the namespace syntax to address a function in the application main or a different module, I you still need to have included the source code file containing that module in the main code, so the problem of sharing functions between different source code files appears to still exist.

At the end of the day I guess any solution that works is the answer, even if it seems to be a bit of a workaround, but I'd like to know that I'm not overlooking something obvious in how projects should best be structured and maintained in the PB IDE.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: When to IncludeFile and when to UseModule?

Post by mk-soft »

To use common functions in the other modules, create a common module, which you then call in the other modules with UseModule Common.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Demivec
Addict
Addict
Posts: 4086
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: When to IncludeFile and when to UseModule?

Post by Demivec »

One possible template for organizing code:

Code: Select all

:include headers for modules and main scope subs
IncludeFile "DeclareModule Common.pbi"
IncludeFile "DeclareModule Module_A.pbi"
IncludeFile "DeclareModule Module_B.pbi"
IncludeFile "DeclareModule Module_C.pbi"
IncludeFile "DeclareModule Module_D.pbi"
IncludeFile "Structures, Constants, prototypes, etc.pbi"
IncludeFile "Main scope Globals & Defines"
IncludeFile "Main scope Declares for procedures.pbi"

;include code for modules and main scope subs
IncludeFile "Module Common.pbi"
IncludeFile "Module Module_A.pbi"
IncludeFile "Module Module_B.pbi"
IncludeFile "Module Module_C.pbi"
IncludeFile "Module Module_D.pbi"
IncludeFile "Main scope Procedures.pbi"
Bitblazer
Enthusiast
Enthusiast
Posts: 733
Joined: Mon Apr 10, 2017 6:17 pm
Location: Germany
Contact:

Re: When to IncludeFile and when to UseModule?

Post by Bitblazer »

Each module is a single file for me. The header has the declare section and the module implementation itself follows. That way i can not miss a part of a module if i pass it to somebody or post or upload it.

My current remainig module problem is that the "usemodule" command is basically pointless for me :(

Any (complex) seperate code collection (module, dll, library etc.) has 2 seperate areas and 3 minimum required routines.
  • Init()
  • one or more routines to do whatever the module is for
  • Exit()
Init() allocates buffers, files, regions, semaphores, verifies the environment - lot of boring household stuff, Exit() closes files, free's handles, connections, semaphores, closes logfiles or connections and in the end exits. The additional code can be between 1 and hundreds of procedures that actually do whatever the module is for.

If i use "usemodule", i have dozens of name collisions because the init and exit procedures all suddenly see each other. So i am currently using module::procedure() instead of "usemodule". But beside this little nuisance, modules are very useful.

In the end, i simply do a

Code: Select all

xincludefile "TextProcessing.pbi"
for each used module in each project.

and to use a module function in a project

Code: Select all

Textprocessing::RemoveTrailingCodes(SomeTextLine$)
Simple example of a very simple module without the implementation code

Code: Select all

DeclareModule TextProcessing
  Declare.i GetNextLineLengthW(*Buffer, MaxLength.i, AllowTerminatingZero.i)
  Declare.i GetNextLineLengthA(*Buffer, MaxLength.i, AllowTerminatingZero.i)
  Declare.s GetNextLineA(*Buffer, DataSize.i, *LineLength, *Result, AllowTerminatingZero.i)
  Declare.s GetNextLineW(*Buffer, DataSize.i, *LineLength, *Result, AllowTerminatingZero.i)
  Declare.i GetLineElementsW(TextLine$, QuotationMark1$, QuotationMark2$, Seperator$)
  Declare.s RemoveTrailingCodes(MyLine$)
  Declare.s GetFollowingParameters(ThisLine$, ParameterNum.i)
EndDeclareModule

Module TextProcessing
  Global NewList LineElements$()
  
  Procedure.s RemoveTrailingCodes(MyLine$)
  EndProcedure
  
  Procedure.i GetNextLineLengthA(*Buffer, MaxLength.i, AllowTerminatingZero.i)
  EndProcedure
  
  Procedure.i GetNextLineLengthW(*Buffer, MaxLength.i, AllowTerminatingZero.i)
  EndProcedure
  
  Procedure.s GetNextLineA(*Buffer, DataSize.i, *LineLength, *Result, AllowTerminatingZero.i)
  EndProcedure
  
  Procedure.s GetNextLineW(*Buffer, DataSize.i, *LineLength, *Result, AllowTerminatingZero.i)
  EndProcedure
  
  Procedure.i GetLineElementsW(TextLine$, QuotationMark1$, QuotationMark2$, Seperator$)
  EndProcedure
  
  Procedure.s GetFollowingParameters(ThisLine$, ParameterNum.i)
  EndProcedure

EndModule
webpage - discord chat links -> purebasic GPT4All
User avatar
SnowyDog
User
User
Posts: 21
Joined: Tue Jun 10, 2014 8:18 pm

Re: When to IncludeFile and when to UseModule?

Post by SnowyDog »

Many thanks for your replies, much appreciated.

I like the idea of using modules for different classes of function but had assumed that using modules, each in a separate source code file, would solve the "include paradox" I was experiencing. It's pretty clear that this isn't one of the benefits of using modules at least in the way I had expected, which is fine now that I know.

The approach I'm taking now is to have a set of IncludeFile statements at the top of my template for a new application main source code, something like: -

Code: Select all

; -------------------------------------------------------------------------------------------------
; Includes (Constants, Structures, Declares, Macros, Function Code)
; -------------------------------------------------------------------------------------------------
XIncludeFile "C:\PureBASIC\Includes\constants.pbi"
XIncludeFile "C:\PureBASIC\Includes\structures.pbi"
XIncludeFile "C:\PureBASIC\Includes\declares.pbi"
XIncludeFile "C:\PureBASIC\Includes\macros.pbi"
XIncludeFile "C:\PureBASIC\Includes\f_generic.pbi"
XIncludeFile "C:\PureBASIC\Includes\f_scsi.pbi"
XIncludeFile "C:\PureBASIC\Includes\f_windisk.pbi"
The declares.pbi file contains all declarations for the functions in the function code files. Local constants, structures, declares and macros associated with just the main application are placed under this section in the main code and before main(). This works fine for me and seems to be as maintainable as my PowerBASIC projects.

Thanks again all for the replies and ideas.

Cheers
Robin
Post Reply