W
W
WayMax2017-03-09 11:33:27
linux
WayMax, 2017-03-09 11:33:27

How to implement cross-platform software with libraries for Windows/Linux?

The software itself will be on QT, but so that ifdefs are not scattered around the code, I wanted to move the OS-dependent code into 2 libraries for Windows and Linux with a common interface (not a GUI, but an API). How to implement it correctly?
Create a library project in Qt Creator? Would it be possible to create a DLL for Windows this way?
UPD:
Project #1: Library for Linux

int MyFileWrite(int file, unsigned char *data, int lengh)
{
    return write(file, data, lengh);
}

Project #2: Library for Windows
int MyFileWrite(int file, unsigned char *data, int lengh)
{
    return fwrite (data, sizeof(char), lengh, file);
}

Project #3: GUI on QT
ifdef WINDOWS
// подключить библиотеку
elseif LINUX
// подключить библиотеку
endif

1. Projects No. 1 and 2 to create and collect in the same way in QT or how?
2. How to include libraries in project No. 3 so that you can use the MyFileWrite function

Answer the question

In order to leave comments, you need to log in

2 answer(s)
E
Evgeny Shatunov, 2017-03-09
@WayMax

I have been improving the game framework since 2011. All of its code is written in C++. There used to be 5 target platforms (Win, MacOs, iOs, Android, Bada), now 4 (Bada closed) with an eye on Tizen, WinMo and, someday, consoles.
In general, the level of requirements for cross-platform should be clear. And here is how I achieved it.
Most of the code is written in platform-independent C++. All platform-specific code is split into three layers:
- Bottom layer, common interface for all platforms, common fields for all platforms.
- Middle layer, platform-specific solutions and fields. Inherited from the bottom.
- Top layer, injecting platform-specific code into the framework. Inherited from the middle layer.
Conditional compilation is used only to include the platform-specific code header. No macros, no conditional compilation is allowed anymore. In platform-specific code, everything is written in open source as if it were written for one platform.
At the file level, this approach works like this.
There is a "platform" folder in the project headers, where the lower levels of splitting are collected, the master header with a conditional connection of the middle splitting level and all common types for platforms.
Also in the project there are folders "platform.windows", "platform.macos", "platform.###" in which the middle level of splitting and master headers for conditional connection are implemented.
The top level is either implemented in its own folder, if it is a whole subsystem, or is described in the same "platform" folder.
The source code is grouped in the same way, but only includes the master header.
Build scripts for each of the platforms include platform-specific code only for their platform.
Everything is collected into static libraries and linked into one executable file. Although there is the possibility of crowding out libraries into dynamic modules (done in case the framework is transferred to outsourcers).
All this gives complete transparency of code execution for any platform. Also, this approach makes it very easy to expand the entire framework to a new platform.
The example with the file fits very well due to its simplicity, I can even write it out from memory from my framework, but I will still simplify something so as not to confuse or scare anyone.

spoiler
// PlatformSpecificFile.Windows.h
class PlatformSpecificFile
{
// Platform-specific interface.
public:
  inline ::HANDLE GetHandle() const	{ return m_handle; };
  
// Platform-independent interface, but platform-dependent implementation.
public:
  // RAII.
  PlatformSpecificFile() = delete;
  PlatformSpecificFile(
    const std::string& path,
    const OpeningMode desired_mode,
    const AccessOptions& desired_access,
    const SharingOptions& desired_sharing
  );
  
  virtual ~PlatformSpecificFile();
  
  void Close();
  void Flush();
  
  const size64_t GetSize() const;
  const bool Resize( const size64_t new_size );
  
  const size32_t Read( NotNull<uint8_t> buffer, const size32_t buffer_size ) const;
  const size32_t Write( NotNull<const uint8_t> buffer, const size32_t buffer_size );
  
  const size64_t Seek( const size64_t offset, const SeekOrientation orientation );
  
  inline const bool IsValid() const	{ return IsHandleValid( m_handle ); };
  
private:
  ::HANDLE	m_handle = INVALID_HANDLE_VALUE;
};

// PlatformSpecificFile.Android.h
class PlatformSpecificFile
{
// Platform-specific interface.
public:
  inline int GetHandle() const	{ return m_handle; };
  
// Platform-independent interface, but platform-dependent implementation.
public:
  // RAII.
  PlatformSpecificFile() = delete;
  PlatformSpecificFile(
    const std::string& path,
    const OpeningMode desired_mode,
    const AccessOptions& desired_access,
    const SharingOptions& desired_sharing
  );
  
  virtual ~PlatformSpecificFile();
  
  void Close();
  void Flush();
  
  const size64_t GetSize() const;
  const bool Resize( const size64_t new_size );
  
  const size32_t Read( NotNull<uint8_t> buffer, const size32_t buffer_size ) const;
  const size32_t Write( NotNull<const uint8_t> buffer, const size32_t buffer_size );
  
  const size64_t Seek( const size64_t offset, const SeekOrientation orientation );
  
  inline const bool IsValid() const	{ return m_handle >= 0; };
  
private:
  int		m_handle = -1;
};

// File.h
class File final : public PlatformSpecificFile
{
public:
  using PlatformSpecificFile::PlatformSpecificFile;
  
  const size64_t GetPosition() const; // Seek( 0, SeekOrientation::FromPosition );
  
  const bool SetPosition( const size64_t position ); // Seek( position, SeekOrientation::FromBeginning );
  
  const bool IsFileEnded() const; // GetPosition() == getSize();
};

The platform code master header "platform.h" includes, depending on the build script, one of the platform code master headers "platform.###.h". The platform specific code already includes the appropriate file header "PlatformSpecificFile.###.h"

S
Stanislav Makarov, 2017-03-09
@Nipheris

How to implement it correctly?

In short, write different implementations of the same headers. You can link statically, you can link dynamically (of course, here you still have to play around with macros to take into account platform-specific things).

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question