2

I have been dealing with a problem for several days and still have no proper solution. Imagine I have several (number can vary) instruments. Each instrument produces data of different type. For each instrument I have a structure (I show very simplistic approximations here):

struct TInstrument1 {
    int Variable1;
    int Variable2;
}

struct TInstrument2 {
    int Variable3;
} 

I am trying to create kind of a database to look through the available data. The way I do this is as follows:

First, I have a class which does filemapping. I am programming on Windows so I use CreateFile, CreateFileMapping,MapViewOdFile functions inside the MapFile function:

template <class SType> class TFileMapping {

    HANDLE    hFile;
    HANDLE    hFileMap;

    SType  *  MapView;

    TFileMapping();
    ~TFileMapping();

    void MapFile(String FileName,DWORD FileAccessType,DWORD FileSharing,DWORD MappingAccessType,DWORD MapViewAccessType,DWORD FlagsAndAttributes,DWORD CreationDisposition,size_t ByteNum);
    void UnMapFile();

};

This class is then use in TMapVector structure (i give a simplified version):

template <class SType> struct TMMapVector : public TFileMapping<SType> {

    String MappedFileName;

    unsigned int   * AssignedLen;
    unsigned int   * Num;
    SType          * Data;

    TMMapVector();
    ~TMMapVector();

    void   MapVector(String FileName);
    void   AppendItem(SType * Val);
    
    int    size();

};

The idea is that for instance TMapVector<Instrument1> creates/opens a database file corresponding to Instrument1 and contains a objects of the type Instrument1.

Now the problem I have is that I do not know how many types of instruments a user has. At the moment there are 3 but the code should be easily extendable to more instrument types. At the moment I handle it as follows. I have a class TFileInfo with both instrument types, but only one of them is filled. This allows me to further use std::vector<TFileInfo>. The problem with this approach is that I have to use switch statements every time I need to do something with instruments:

struct TFileInfo {

    TMMapVector<TInstrument1>  Instrument1;
    TMMapVector<TInstrument2>  Instrument2;

};

struct TDataBase {
    std::vector<TFileInfo> FileInfo;
    std::vector<char>      InstrumentType;
    
    int GetNumberOfItemsInArchive(int ArchIdx);
}

int TDataBase::GetNumberOfItemsInArchive(int ArchIdx) {
    switch InstrumentType[ArchIdx] {
        case 1:
            return(FileInfo[ArchIdx].Instrument1.size());
        case 2:
            return(FileInfo[ArchIdx].Instrument2.size());
    }
}

Just imagine if I need to add another 10-100 instruments and of course I have not just the size() function but many more. Is there a simpler way to implement this? I was thinking about interfaces but (1) if I understand it right virtual functions are not usable with templates and (2) I do the same operations with the data, only the type is different. I would highly appreciate an advice.

Alexander
  • 43
  • 5
  • 1
    `Now the problem I have is that I do not know how my types of instruments a user has` does this mean you don't know the number of instruments or you dont know the number of types of instruments? i suspect you have a type somewhere in that sentence :) – Exagon Aug 28 '20 at 08:59
  • @Exagon Sorry for being not precise enough. I do not know a number of instruments, a user should be able to add/remove the instruments. And regarding the types, at the moment I have a certain number of instruments implemented. But it can happen that customers can ask to add more instrument types in the future, so the code must the easy to extend. This is what I meant by saying that the number of types is not known. – Alexander Aug 28 '20 at 09:04
  • Why aren't you utilizing virtuality? Looks like exactly what you need. – freakish Aug 28 '20 at 09:04
  • @freakish Could you please explain it a bit more? I read that virtual functions cannot be used with templates. – Alexander Aug 28 '20 at 09:06
  • 1
    That `__fastcall` looks suspicious. It can't be there because `&TFileMapping::MapFile` is used as a callback for a Windows function. Windows doesn't understand pointer to member functions at all, so `__fastcall` is pretty pointless. – MSalters Aug 28 '20 at 09:25
  • @Alexander You can use virtual functions with templates but you usually need a common base class to define the interface. That class is usually not created from a class template. Example: https://godbolt.org/z/KGo1o6 – Ted Lyngmo Aug 28 '20 at 09:31
  • @MSalters Thanks for thepoint. However, the compiler does not complain. I remove __fastcall this from the code above. – Alexander Aug 28 '20 at 09:32
  • @TedLyngmo I agree, I was thinking about this. But I guess I cannot make functions depending on the type (like ```AppendItem(SType * Val)```) virtual. – Alexander Aug 28 '20 at 09:36
  • 1
    @Alexander [Sure you can](https://godbolt.org/z/r8vK46), but you can't make it part of the `Base` interface. `virtual` functions can't be function templates, but `virtual` functions can be a part of a `class` template. – Ted Lyngmo Aug 28 '20 at 09:51
  • @TedLyngmo ok, thanks for the clarification. But if I have virtual functions in the ```TMMapVector``` template, how would this help me to simplify the code? I will still have to use switch statements. – Alexander Aug 28 '20 at 10:10
  • @Alexander `void TMMapVector::AppendItem(SType* Val);` will only accept `SType*` or pointers to types derived from `SType` if I read the code correctly. – Ted Lyngmo Aug 28 '20 at 10:16
  • @TedLyngmo SType could be Intrument1,Instrument2, Instrument3.... and so on. There will be no inheritance from these types. – Alexander Aug 28 '20 at 10:19
  • @Alexander Ok, so if you have a collection of `TMMapVector` or classes derived from it, you can safely call `AppendItem` with an `SType` pointer. If you try with the wrong `SType` you'd get a compilation error. – Ted Lyngmo Aug 28 '20 at 10:36
  • @TedLyngmo yes, exactly. Therefore, I have to use a class ```TFileInfo``` with all possible TMMapVector,TMMapVector.... and use switch statements in each function.This I want to avoid. – Alexander Aug 28 '20 at 10:39
  • @Alexander Wouldn't it be appropriate to have all the instruments derive from a `BaseInstrument` which defines their most basic properties and actions (a `virtual apply()` or something) to keep the checking down to a minimum? – Ted Lyngmo Aug 28 '20 at 10:44
  • @TedLyngmo This was among my first ideas. The problem with this approach is that these instrument objects need to be stored in files and I make this using the filemapping approach. A virtual function adds an absolute pointer to the vtable into the object. This pointer is then stored in the file. But this pointer will not be valid any longer when I restart the application. – Alexander Aug 28 '20 at 10:47
  • 1
    @Alexander No, you'd need to add serializing support to save/restore your instance-tree somehow. Perhaps you could use an existing library for it, or write your own support classes for it. `type#hash_or_address#instance_data` could perhaps be written to file and when restored you'd need to map `hash_or_address` to whatever the new instances gets to reconnect the pointer tree. I haven't thought that through. There are probably pitfalls :-) – Ted Lyngmo Aug 28 '20 at 11:03

1 Answers1

0

The root cause is that you can't do introspection on a struct.

It may be better to use class TInstrument1 : public std::tuple<int, int>. This will give you access to the whole machinery of <tuple>.

Having said that, this might be a challenge for you. Code like unsigned int * Num; and int size(); suggests that you're a beginning coder. The better advice in that situation might be to use a standard database instead.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Thanks for your answer, right, I am not a professional programmer, I am just learning. Is it possible to store objects of type tuple into a mapped file? As far as I know it would not work for example with vectors. – Alexander Aug 28 '20 at 09:43
  • 1
    @Alexander: Considering how you phrase that question, I'm going to answer "no". The reason for that is that it _does_ work with vectors, but not with `std::vector>`. You need an appropriate memory allocator for the mapped memory. `std::tuple` also understands memory allocators. See https://stackoverflow.com/questions/24901114/memory-mapped-file-storage-in-stl-vector for an existing approach. – MSalters Aug 28 '20 at 10:00
  • I think I get your point, thanks. So, it is possible to make the vector/tuple work with filemapping by creating a proper allocator. But it think this is a bit different topic. My main question is how can I avoid using ```TFileInfo``` and the switch statements related to it? – Alexander Aug 28 '20 at 10:16
  • @Alexander: I'm still struggling to understand exactly what you are trying to achieve, honestly. The instrument part, I see. But then you go on to say that a user may have three different types of instruments, yet each `TMMapVector` can only hold one type. My gut reaction is to dump that `TMMapVector` template, and everything that comes afterwards. But I figured from your style you're probably also unfamiliar with `std::shared_ptr`. In general, my problem is that the advice appropriate for the problem doesn't math the advice appropriate to your experience. – MSalters Aug 28 '20 at 10:34
  • Rather than inheriting from `std::tuple`, you could provide specialisations for `std::get`, `std::tuple_size` and `std::tuple_element` – Caleth Aug 28 '20 at 12:07