Static Library i dependent (internal) headers

Static Library i dependent (internal) headers
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:12 miesięcy
0

Pierwszy raz w życiu piszę własnego static lib'a i nie mam pojęcia coś takiego zmontować do kupy. Internet jest pełen przykładów, ale wszędzie robią to w najprostszej formie, czyli deklaracja funkcji w .h, definicja w .cpp i tyle. Mnie natomiast zastanawia jak w bibliotece umieścić klasę, która będzie miała dostęp do innych klas (każda w osobnym .h i .cpp), ale bez konieczności dystrybuowania ich nagłówków razem z biblioteką. Coś takiego:

Kopiuj
//Core.h

#include "Point.h"
#include "Triangle.h"

class Core
{
  Point P;
  Triangle T;
}

Chciałbym to zorganizować tak, żeby dołączać sam .lib i Core.h ale już nie dziesiątki innych header'ów jak Point.h, Triangle.h itd. One i tak nie są przeznaczone dla końcowego użytkownika, tylko na wewnętrzne potrzeby klasy Core.

Jak coś takiego się robi?

_13th_Dragon
Ot tak bez strażników?
CR
To tylko przykład, pisany "z ręki", nie wklejka.
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:6 dni
2

Nie możesz wmontować *.h do *.lib. Do biblioteki pakujesz kod, czyli funkcje.

Kopiuj
//AllMy.h
#include "Point.h"
#include "Triangle.h"
#include "Core.h"

Wszędzie dołączasz tylko #include "AllMy.h"


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 1x, ostatnio: _13th_Dragon
CR
Tu nie chodzi o samo #include, tylko o dystrybucję plików. Chciałbym po prostu uniknąć konieczności przekopiowywania 50 różnych headerów za każdym razem, gdy chcę skorzystać z biblioteki.
_13th_Dragon
Zawsze możesz użyć .TLB - OLE Type Library
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:12 miesięcy
0
_13th_Dragon napisał(a):

Nie możesz wmontować *.h do *.lib. Do biblioteki pakujesz kod, czyli funkcje.

Wiem, ale mnie właśnie zastanawia czy da się to jakoś obejść. Biblioteki często są dystrybuowane tylko z plikiem .lib i pojedynczym nagłówkiem, choć wewnętrznie korzystają z wielu klas.

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:6 dni
1

Jeżeli dana klasa nie wychodzi poza wewnętrzne bebechy biblioteki to nie ma żadnego problemu, ale jak widzę ty chcesz mieć te *.h na zewnątrz biblioteki.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
CR
Niekoniecznie, chciałbym po prostu, żeby kod z pierwszego posta działał i linker nie krzyczał, że nie może znaleźć Point.h i Triangle.h. Kompiluje się dopiero po ich przekopiowaniu razem z Core.h, a tego właśnie chciałbym uniknąć.
_13th_Dragon
Biblioteka nie zawiera deklaracji koniec kropka. Tak czy owak musisz deklaracje Point.h Triangle.h udostępnić. Zawsze możesz podać dodatkową ścieżkę dla #include aby nie kopiować tych plików.
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:19 dni
  • Lokalizacja:Wrocław
  • Postów:399
2

Wygląda na to, że potrzebujesz interfejsów i implementacji interfejsów w bibliotece.

Nie ma jednego konkretnego sposobu na zrobienie tego. Zwłaszcza kiedy mowa o kompatybilności wstecznej.

Często implementuje się to używając metod wirtualnych.

Drzewo plików:

Kopiuj
Public/Core.h
Private/CoreImpl.h
Private/Point.h
Private/Triangle.h
Private/CoreImpl.cpp

To co jest w Public/ jest dla użytkownika końcowego biblioteki. To co jest w Private/ jest implementacją biblioteki.
Często stosowaną konwencją są określenia include (odpowiednik Public), i src (odpowiednik Private).

Kopiuj
// Public/Core.h
#pragma once

class Core
{
public:
  virtual void SomeMethod() = 0;
};

Core *CreateCore();
void DestroyCore( Core *core );

Kopiuj
// Private/Point.h
#pragma once

class Point
{
};
Kopiuj
// Private/Triangle.h
#pragma once

class Triangle
{
};
Kopiuj
// Private/CoreImpl.h

#include "Point.h"
#include "Triangle.h"

class CoreImpl : public Core
{
public:
  virtual void SomeMethod() override;

private:
  Point m_point;
  Triangle m_triangle;
};
Kopiuj
// Private/CoreImpl.cpp

#include "CoreImpl.h"

void CoreImpl::SomeMethod()
{
 // Implementacja funkcji Core::SomeMethod()
}

Core *CreateCore()
{
  return new CoreImpl;
}

void DestroyCore( Core *core )
{
  delete static_cast< CoreImpl * >( core ); // Jeśli nie chcesz tutaj static_cast'a, to destruktor Core musi być wirtualny
}

Z punktu widzenia użytkownika:

Kopiuj

#include "Core.h"

int main()
{
  Core *core = CreateCore();

  core->SomeMethod();

  DestroyCore( core );
}

Użytkownik dostaje tylko zawartość Public/ i .lib, nie zna zawartości Private/.

Można też to robić inaczej, gdzie kod C++ jest wrapperem tylko na kod C. Polecam zajrzeć do tego jak jest zrobiony Discord SDK (https://discord.com/developers/docs/game-sdk/sdk-starter-guide ) (download: https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip)


Asm/C/C++
edytowany 9x, ostatnio: mwl4
CR
No ale to chyba nie eliminuje potrzeby dystrybuowania wszystkich headerów razem z plikiem .lib?
mwl4
@Crow: Dystrybuujesz tylko to co w Public/ i .lib. Czyli w tym przykładzie tylko Core.h i .lib. Pliki Point.h, Triangle.h, CoreImpl.h, CoreImpl.cpp nie są znane dla użytkownika końcowego biblioteki (stanowią implementację).
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:19 dni
  • Lokalizacja:Wrocław
  • Postów:399
1

Drugim sposobem, żeby to zrobić jest ukrycie zależności pod wskaźnikiem (tak to robi na przykład Qt):

Drzewo plików:

Kopiuj
Public/Core.h
Private/CoreImpl.h
Private/Point.h
Private/Triangle.h
Private/CoreImpl.cpp
Kopiuj
// Public/Core.h
#pragma once

class Core
{
public:
  Core();
  ~Core();

  void SomeMethod();

private:
  class Impl;
  Impl *m_impl;
};
Kopiuj
// Private/Point.h
#pragma once

class Point
{
};
Kopiuj
// Private/Triangle.h
#pragma once

class Triangle
{
};
Kopiuj
// Private/CoreImpl.h
#pragma once

#include "Core.h"
#include "Point.h"
#include "Triangle.h"

class Core::Impl
{
public:
  void SomeMethod();

private:
  Point m_point;
  Triangle m_triangle;
};
Kopiuj
// Private/CoreImpl.cpp
#include "CoreImpl.h"

Core::Core()
{
  m_impl = new Impl;
}

Core::~Core()
{
  delete m_impl;
}

void Core::SomeMethod()
{
  return m_impl->SomeMethod();
}

void Core::Impl::SomeMethod()
{
  // Implementacja funkcji Core::SomeMethod()
}

Z punktu widzenia użytkownika:

Kopiuj
#include "Core.h"

int main()
{
  Core core;
  core.SomeMethod();
}

Asm/C/C++
edytowany 2x, ostatnio: mwl4
CR
To się nie kompiluje, nie da rady stworzyć w ten sposób definicji klasy Core::Impl.
mwl4
@Crow: jaki masz błąd? Dodałem jeszcze #include "Core.h" do Private/CoreImpl.h. Kod pisałem z palca, ale powinien działać.
CR
Teraz działa, brakowało właśnie tego nagłówka :).
ZD
  • Rejestracja:około 3 lata
  • Ostatnio:ponad rok
  • Postów:2310
0

@Crow:

Headery schodzące do każdej klasy, to chyba pomyliłeś języki.
Może jak w laboratorium robimy proof of concept gdy dynamicznie żyje, ale już nigdy potem w kodzie udostępnionym.
.
najbardziej optymistyczny podział headerów, 1 plik ~= coś a'la pakiet / moduł

ps. nie byłbym sobą, gdybym nie dodał, że sama koncepcja include sięga lat 1950ch i jest od pięć pięter bardziej prymitywna od importu (tak to się nazywa w większości, using, use itd )


If you put a million monkeys at a million keyboards, one of them will eventually write a Java program - the rest of them will write Perl
CR
Dlaczego miałbym pomylić języki? U mnie kod się kompiluje, jeżeli razem z .lib nie ma wszystkich headerów.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.