Reputation: 1824
I have 2 projects that work on same data, different processes. Main arrays are almost identical with slight variations, like:
// Project 1
TData1 = record
A:string;
B:integer;
C:word;
...
end;
// Project 2
TData1 = record
A:string;
B:integer;
C:word;
...
XMLNode:TXMLNode; // Extra value needed in Project 2, not needed in Project 1
end;
I have numerous arrays that i want to share between projects. I would like to keep the same array structure so I can copy&paste any future changes that need to be implemented in both projects. Is there any way to keep records same with slight difference?
I was thinking of something like this:
// in Project 1:
TExtras = record
end;
// in Project 2:
TExtras = record
XMLNode:TXMLNode;
end;
// shared - in both projects
TData1 = record
A:string;
B:integer;
C:word;
...
Extras:TExtras; // different extra fields based on project needs
end;
And i can add additional fields into Extras in Project2 accessing fields with Data1.Extras.XMLNode
. Not sure if this is future proof implementation.
The goal is to one day put all the shared structure into shared unit, one maintenance point and no more copy&paste. I need arrays to retain flexibility as simple arrays (array of TData1
or TArray<TData1>
), so I can't go into complex implementation that will limit option to easily copy, sort, make distinct, manipulate data...
Is that correct approach? Any better ideas?
Edit:
Both projects work on same data, so they both read from the same 'source' files, but produce different end results. Right now I have lots of arrays and 99% of them are used for the same purpose in both projects, same functions. But now, when I work on one or the other project, adding new record fields, new functions that use new fields, and if I don't immediately synchronize the structure and new functions, it happens that in a few weeks I will need to to do the same in project 2 and I will create new fields with different names and different function names. So, when I finally copy some complex function between projects, I see they don't match only due to different naming.
Edit 2:
From all the comments and suggestions I have decided to go another route: to share common data structure and code in shared unit and create additional arrays with extra record fields in project 2. I would create these new arrays that link to main data arrays, to have:
// shared data
TData1 = record
A:string;
B:integer;
C:word;
...
end;
Data1:TArray<TData1>;
// additional in Project 2
TDataExtra = record
DataIdx:integer;// link to TData1
XMLNode:TXMLNode;
...
end;
DataExtras:TArray<TDataExtra>;
to have simple access to XMLNode value for each Data1 record:
fGetXMLNode(i); // where i is index in Data1 array and function will return XMLNode
I believe with this I can keep shared units and add any extras to any array, with the minimal extra work, which is still lower cost than maintaining 2 data structure and code.
Would that be better solution?
Upvotes: 2
Views: 240
Reputation: 1824
Easy solution is to keep the data structure and related functions shared between both projects and add additional arrays that contain additional data, specific to each project. It will bring slightly more work to use extra arrays, but will keep shared code really shared.
So, instead of new classes or IFDefs, as proposed in other anwers, a simple extra arrays that link to main data are best option for the problem:
// shared main data through both projects
TData1 = record
A:string;
B:integer;
C:word;
...
end;
Data1:TArray<TData1>;
// additional in Project 2
TDataExtra = record
DataIdx:integer;// link to TData1
XMLNode:TXMLNode;
...
end;
DataExtras:TArray<TDataExtra>;
With this solution, any additional arrays, like DataExtras or any other extra fields for other arrays, are easy to add, expand, without needing to change shared code. Shared code will be easy to maintain, as it only holds main data and nothing specific to only one project
Upvotes: 2
Reputation: 16065
Pascal does not have void/Unit datatype, still you can kind of simulate it.
The compiler does not always feel nice about it, in some paths it just makes no assumption/check that in some extreme cases SizeOf(someType)
might be zero.
Still, if you really need to squeeze every last byte in Project 2 while keeping sources shared with Project 1 there can be used this ugly hack:
File Project_1.inc
{$IFnDEF THIS_IS_PROJECT_1}
halt compilation! wrong setup!!!
{$EndIf}
{$IFDEF THIS_IS_PROJECT_2}
halt compilation! wrong setup!!!
{$EndIf}
type TDataPayload = TXMLNode;
File Project_2.inc
{$IFnDEF THIS_IS_PROJECT_2}
halt compilation! wrong setup!!!
{$EndIf}
{$IFDEF THIS_IS_PROJECT_1}
halt compilation! wrong setup!!!
{$EndIf}
type REmpty = packed record end;
type TDataPayload = REmpty;
// after compilation - call `assert(SizeOf(TDataPayload) = 0);`
File CommonDataType.pas
{$IfDef THIS_IS_PROJECT_1}
{$INCLUDE Project_1.inc}
{$EndIf}
{$IfDef THIS_IS_PROJECT_2}
{$INCLUDE Project_2.inc}
{$EndIf}
TData1 =
{$IFDEF THIS_IS_PROJECT_2}
packed
{$EndIf}
record
A:string;
B:integer;
C:word;
...
Payload: TDataPayLoad;
end;
That being said, your assessment that you can not waste a single byte via type TXMLNode=byte
compatibility stub in the project 2 seems very dubious to me. Because:
record
instead of slow bytes-squeezing packed record
A:string;
spends 4 (or 8) bytes per the pointer inside every record and then 12 bytes more as StringRec
header, and then unknown pre-allocated just-in-case buffer after the string content, and then two extra bytes for trailing #0, and then supporting data structures in the Heap Manager. Still just ONE extra byte of type TXMLNode=boolean
stub is way too much???Upvotes: 2
Reputation: 6502
It is going to be hard to say which solution would be best/better without the full context, but here's a few other methods that can achieve the same.
Conditional define
TData1 = record
A:string;
B:integer;
C:word;
[...]
{$IFDEF NEEDXMLNODEINTDATA1}
XMLNode:TXMLNode; // different extra fields based on project needs
{$ENDIF}
end;
Use a new structure
Use a different structure in your 2nd application. I would expect this to be the right approach for most cases.
TData1Node = record
Data1 : TData1;
XMLNode : TXMLNode;
end;
Include XMLNode all the time
Since it's just a pointer, unless you have an absurd amount of records in your application, it would barely register on memory usage.
Upvotes: 2