Nope, no copying.
I had tested beforehand deleting the copy operators, just as you recommend, wintertime, but no compiler errors.
Here, now we have a code that anyone can test!
#include <vector>
#include <cmath>
#include <stdexcept>
#include <iostream>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
// ---
class Tileset {
public:
Tileset (unsigned int tileWidth, unsigned int tileHeight)
: m_tileSize(tileWidth, tileHeight)
{
m_drawer.setTexture(m_texture);
}
Tileset (const Tileset&) =delete;
Tileset& operator=(const Tileset&) =delete;
~Tileset() {
std::cout << "I will die horribly.\n\n";
std::terminate();
}
bool loadFromFile (const std::string& path) {
if (m_texture.loadFromFile(path)) {
prepareTiles();
return true;
}
return false;
}
bool loadFromMemory (const void* data, size_t size) {
if (m_texture.loadFromMemory(data, size)) {
prepareTiles();
return true;
}
return false;
}
void drawTile (int index, const sf::Vector2u& posId, sf::RenderTarget& tar) const {
auto where = m_tileSize;
where.x *= posId.x;
where.y *= posId.y;
m_drawer.setTextureRect(getTile(index));
m_drawer.setPosition(where.x, where.y);
tar.draw(m_drawer);
}
size_t getTileCount() const {
return m_tiles.size();
}
const sf::IntRect& getTile (int index) const {
return m_tiles.at(index);
}
private:
void prepareTiles() {
m_texture.setSmooth(true);
sf::Vector2u textureSize = m_texture.getSize();
sf::Vector2u nTiles = sf::Vector2u (
std::ceil(static_cast<float>(textureSize.x) / m_tileSize.x),
std::ceil(static_cast<float>(textureSize.y) / m_tileSize.y)
);
m_tiles.clear();
for (int row = 0; row < nTiles.y; row++) {
for (int col = 0; col < nTiles.x; col++) {
m_tiles.emplace_back (
col * m_tileSize.x,
row * m_tileSize.y,
m_tileSize.x,
m_tileSize.y
);
}
}
}
sf::Vector2u m_tileSize;
mutable sf::Sprite m_drawer;
sf::Texture m_texture;
std::vector<sf::IntRect> m_tiles;
};
// ---
#include <map>
#include <string>
#include <memory>
#include <fstream>
#include <iterator>
// ---
class LevelData {
public:
static std::shared_ptr<const Tileset> getTileset (const std::string& tilesetName, const sf::Vector2u& tileSize) {
auto found = tilesets.find(tilesetName);
if (found != tilesets.end()) {
return found->second;
}
else {
auto tileset = std::make_shared<Tileset>(tileSize.x, tileSize.y);
#define TAKE_IT_EASY
// #define CPP_FILES
#ifdef TAKE_IT_EASY
if (!tileset->loadFromFile(tilesetName)) {
std::cerr << "Tileset couldn't load!\n\n";
std::terminate();
}
#elif defined(CPP_FILES)
std::ifstream stream(tilesetName, std::ios::binary);
if (!stream) {
std::cerr << "Couldn't open the file.\n\n";
std::terminate();
}
std::vector<char> buffer(
(std::istream_iterator<char>(stream)),
(std::istream_iterator<char>())
);
if (!tileset->loadFromMemory(buffer.data(), buffer.size())) {
std::cerr << "Tileset couldn't load!\n\n";
std::terminate();
}
#else
std::FILE *file;
if (!(file = std::fopen(tilesetName.c_str(), "rb"))) {
std::cerr << "Couldn't open the file.\n\n";
std::fclose(file);
std::terminate();
}
auto beg = std::ftell(file);
std::fseek(file, 0, SEEK_END);
auto size = std::ftell(file);
std::fseek(file, beg, SEEK_SET);
char buffer[size];
std::fread(buffer, 1, size, file);
if (!tileset->loadFromMemory(buffer, size)) {
std::cerr << "Tileset couldn't load!\n\n";
std::fclose(file);
std::terminate();
}
std::fclose(file);
#endif
tilesets.insert({ tilesetName, tileset });
return tileset;
}
}
private:
typedef std::map<std::string, std::shared_ptr<Tileset>> TilesetMap;
static TilesetMap tilesets;
};
LevelData::TilesetMap LevelData::tilesets;
// ---
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/RenderTexture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Window/Event.hpp>
int main() {
sf::RenderWindow window(sf::VideoMode(640, 480), "test");
sf::RenderTexture tex;
tex.create(640, 480);
sf::Sprite sprite;
sprite.setTexture(tex.getTexture());
if (auto tileset = LevelData::getTileset("tile.png", sf::Vector2u(45, 45))) {
for (sf::Vector2u pos(0, 0); pos.x < tileset->getTileCount(); ++pos.x) {
tileset->drawTile(0, pos, tex);
}
}
tex.display();
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed: return 0;
}
}
window.clear();
window.draw(sprite);
window.display();
}
}
At LevelData::getTileset, you can toggle those defines at will. In the actual code of my project I am using the "C files" configuration because I just can't slurp data out of C++ files without getting it corrupted - mainly, it eats away any blank characters. If the file has "a b c" as its contents, only "abc" will be extracted. This bug manifested itself even when I executed this stand-alone code in a new "SFML configured" CodeBlocks project. And yes, I have attested that the TAKE_IT_EASY version has problems in my friends' Windows XP computers.
Also, in the actual code, "terminates" are exceptions that I throw, and Tileset's destructor only terminates the program as proof that no Tilesets are destroyed in inappropriate times. You can also remove that once the point comes across and it has become annoying.
getTileset is actually called after a lot of XML parsing by LevelData, but it's nothing that should actually interfere with the functionalities of getTileset and the Tilesets themselves.
Attached is the tile.png I used.