You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
5.3 KiB

//#define _GNU_SOURCE
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include "../include/opaque.hh"
static void s_puts(const auto& string)
{
std::string_view str{string};
#ifdef DIRECT_WRITE
write(1, str.data(), str.size());
#else
printf("%.*s", (int)str.size(), str.data());
#endif
}
static void print_view(std::string_view ptr)
{
//std::string_view ptr{string};
size_t sz;
#ifdef DIRECT_WRITE
// Bypass all caching of the output and immediately invoke `write()` for everything
if(__builtin_expect((sz = ptr.size()) > 0, true)) {
write(1, "> ", 2);
write(1, ptr.data(), sz);
} else write(1, "! <null>\n", 8);
write(1, "\n", 1);
#else
// All the output from the program is cached and `write()` is only called once at the end.
if(__builtin_expect((sz = ptr.size()) > 0, true)) {
printf("> %.*s\n", (int)sz, ptr.data());
} else puts("! <null>\n");
#endif
}
static inline void print(const std::string& string)
{
std::string_view sv{string};
return print_view(sv);
}
enum class moh_hck {
Template,
Ref,
Owned,
};
/// Example using `make_opaque_handle`, like would be done for a C interface
template<moh_hck Create = moh_hck::Owned>
static void use_moh()
{
std::string* str = new std::string(__PRETTY_FUNCTION__);
constexpr auto v_print = [](const opaque_handle& v) {
print(*v);
{
auto copied = v;
s_puts("(copied)");
print(*copied);
s_puts("(moved)");
print(*std::move(copied));
}
};
if constexpr(Create != moh_hck::Owned) {
constexpr auto _h = [](std::string* ptr, auto op) noexcept {
if(ptr) {
switch(op) {
case opaque_handle_operation::Clone: return new std::string(std::as_const(*ptr));
case opaque_handle_operation::Delete: delete ptr; break;
}
}
return static_cast<std::string*>(nullptr);
};
if constexpr(Create == moh_hck::Template) {
const opaque_handle v{make_opaque_handle<std::string, _h>(str)};
v_print(v);
} else /*if constexpr(Create == moh_hck::Ref)*/ {
const opaque_handle v{make_opaque_handle<std::string>(str, _h)};
v_print(v);
}
} else {
const opaque_handle v{make_opaque_handle<std::string>(str, [](std::string* ptr, auto op) noexcept {
if(ptr) {
switch(op) {
case opaque_handle_operation::Clone: return new std::string(std::as_const(*ptr));
case opaque_handle_operation::Delete: delete ptr; break;
}
}
return static_cast<std::string*>(nullptr);
})};
v_print(v);
}
s_puts("---\n");
}
// Testing a hypothetical C interface
extern "C" {
struct c_struct_example;
struct opaque;
void black_box(const opaque* __restrict__){}
c_struct_example* cs_create(const c_struct_example* __restrict__ from);
void cs_free(c_struct_example* p);
void cs_print(const c_struct_example* p);
}
static void use_c_struct()
{
using type = c_struct_example;
auto* allocp = cs_create(nullptr);
opaque_handle impossible{make_opaque_handle<opaque, opaque_empty_handler<opaque>>(nullptr)};
black_box(impossible);
const opaque_handle v{make_opaque_handle<type>(allocp, [](auto* ptr, auto op) noexcept {
if(ptr) {
switch(op) {
case opaque_handle_operation::Clone: return cs_create(ptr);
case opaque_handle_operation::Delete: cs_free(ptr); break;
}
}
return static_cast<type*>(nullptr);
})};
s_puts("> ");
cs_print(v);
{
auto copied = v;
s_puts("(copied)> ");
cs_print(copied);
s_puts("(moved)> ");
cs_print(std::move(copied));
}
}
int main()
{
const
opaque_handle v{make_opaque_object_handle<std::string>("Hello world")};
puts("Auto-downcasting to `const std::string&`:");
print(*v);
#if 1
s_puts("\nAttempting invalid safe cast... ");
try {
print_view(*v); // Try to cast to std::string_view, not the correct type...
s_puts(" FAILED\n");
return 1;
} catch(opaque_bad_cast&) { // Expected this exception to be thrown
s_puts(" OK\n");
} catch(opaque_exception& other) {
s_puts(" UNEXPECTED (");
s_puts(other.what());
s_puts(")\n");
return 2;
}
#endif
puts("\nConverting view directly:");
print_view(std::string_view{v.cast<std::string, opaque_handle::CAST_UNSAFE /* SAFETY: We know this is the type of `v`'s pointee, so we don't need the type check. */>()}); // Explicit cast needed, *v will choose the wrong type if not.
puts("\nUsing opaque_make_handle():");
use_moh<moh_hck::Template>();
use_moh<moh_hck::Ref>();
use_moh<moh_hck::Owned>();
puts("\nUsing a C interface struct:");
use_c_struct();
return 0;
}
extern "C" {
struct c_struct_example {
int i,j;
size_t sz;
char buf[256];
};
c_struct_example* cs_create(const c_struct_example* __restrict__ from)
{
auto* ptr = reinterpret_cast<c_struct_example*>(aligned_alloc(alignof(c_struct_example), sizeof(c_struct_example)));
//memset(ptr, 0, sizeof(c_struct_example);
if(!from) {
ptr->i = 10;
ptr->j = 11;
snprintf(ptr->buf, 256, "Hello from C interface");
} else {
//memcpy(ptr, from, sizeof(c_struct_example));
*ptr = *from;
ptr->i +=1;
ptr->j -=1;
snprintf(ptr->buf, 256, "Hello from copied C interface (from %p)", from);
}
return ptr;
}
void cs_print(const c_struct_example* p)
{
#ifdef DIRECT_WRITE
char ibuf[512];
size_t n = snprintf(ibuf, 512, "%s: (%d, %d)", p->buf, p->i, p->j);
write(1, ibuf, n < sizeof(ibuf) ? n : 511);
write(1, "\n", 1);
#else
printf("%s: (%d, %d)\n", p->buf, p->i, p->j);
#endif
}
void cs_free(c_struct_example* p)
{
free(p);
}
}