로딩중...
검색중...
일치하는것 없음
Indep

Platform Dependent Layer Platform-dependent code layer that contains all platform-specific implementations. This layer handles all platform-specific code and enables the rest of the system to operate platform-independently. Conditional compilation (#IF, #ELSE, etc.) is only permitted in this module. 더 자세히 ...

네임스페이스

namespace  by::platformAPI
 Platform-independent API wrapper for OS-specific operations
 

클래스

class  by::fsystem
 Cross-platform filesystem utilities for recursive file traversal 더 자세히 ...
 
class  by::buildFeature
 Provides build-time information and feature detection 더 자세히 ...
 
struct  by::buildFeature::date
 Build date information 더 자세히 ...
 
struct  by::buildFeature::time
 Build time information 더 자세히 ...
 
struct  by::buildFeature::version
 Version information 더 자세히 ...
 
struct  by::buildFeature::platform
 Platform information 더 자세히 ...
 
struct  by::buildFeature::config
 Build configuration information 더 자세히 ...
 
class  by::cpIter
 Codepoint-based string iterator 더 자세히 ...
 
class  by::dlib
 Dynamic library loading and management class 더 자세히 ...
 
class  by::dumpable
 Interface for objects that can dump their state 더 자세히 ...
 
class  by::end
 Deferred execution utility similar to defer keyword in other languages 더 자세히 ...
 
class  by::errLv
 Error level definitions and utilities 더 자세히 ...
 
class  by::errorable
 Interface for objects that can report errors 더 자세히 ...
 
class  by::line
 Manages hierarchical depth visualization for logging and tracing. 더 자세히 ...
 
class  by::tmay< T >
 Optional value wrapper for error indication without exceptions 더 자세히 ...
 
class  by::tmedium< T >
 Medium class used exclusively in the OR macro for safe reference handling 더 자세히 ...
 
class  by::tres< T, R >
 Template result container with typed error information 더 자세히 ...
 
struct  by::typeTrait< T >
 Type trait utilities for template metaprogramming 더 자세히 ...
 
struct  by::Initiator
 Utility for executing code before main() function 더 자세히 ...
 

매크로

#define _ON_EACH_DECL(cmd)   __BY__DECL_##cmd
 byeol universal macro
 
#define __BY__DECL_ME_1(ME)   BY_ME_1(ME)
 byeolMeta macro's sub-commands, ME
 
#define BY_DEF_ME_2(ME, SUPER)   __BY__DECL_DEF_ME_2(ME, SUPER)
 byeolMeta macro's sub-commands, DEF_ME
 
#define __OR_DO__(_expr_)
 OR macro
 
#define BY_OVERLOAD(NAME, ...)   BY_CONCAT(NAME##_, __OVERLOAD_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
 Macro Overloding:
 
#define BY_PAIR_1(x)   x
 Funnel
 
#define BY_SIDE_FUNC(...)   BY_OVERLOAD(BY_SIDE_FUNC, __VA_ARGS__)
 Side function macros for safe pointer operations
 
#define TO(fn)
 
#define __WHEN_OBJECT__   __indep_when__
 Early-return pattern macro for exception handling
 

상세한 설명

Platform Dependent Layer Platform-dependent code layer that contains all platform-specific implementations. This layer handles all platform-specific code and enables the rest of the system to operate platform-independently. Conditional compilation (#IF, #ELSE, etc.) is only permitted in this module.

매크로 문서화

◆ __BY__DECL_ME_1

#define __BY__DECL_ME_1 ( ME)    BY_ME_1(ME)

byeolMeta macro's sub-commands, ME

it defines 2 typedefs, me and super. me is same type to typeof(this) class in c++. and super is literally superclass of me. those typedefs are also available in byeol language.

This is part of an effort to make the environment of the byeol language and the C++ environment as consistent as possible.

◆ __OR_DO__

#define __OR_DO__ ( _expr_)
값:
| [&](auto&& __p) -> void { \
__orStack__::push(nul(__p) ? _expr_ : false); \
}; \
if(__orStack__::pop())

OR macro

It enables return ?:, a type of safe navigation in modern languages, in C++. prerequisites: OR macro is based on WHEN macro. Before learning about OR, you need to know WHEN first.

usage: OR macro is used like this: <expr-evalution-as-pointer> OR.<when-expr> in this case, expr-evaluation-as-pointer must evaluate to a pointer type of a certain type. if not, a compilation error occurs. an expression using the WHEN macro is placed after OR, and is executed instead if the previous pointer was nullptr. if the pointer was not nullptr, it is returned as a reference. as a result, using the OR macro, you can safely perform nullcheck and handle non-null types.

int* foo();
int main() {
// without OR:
int* value = foo();
if(!value) return -1;
return *value;
// with OR:
int& value = foo() OR.ret(-1) // If foo() returns nullptr, return
-1.
// now, you don't need to dereference `value` everytime!
return value;
}

the WHEN macro provides various methods in addition to simply returning an error. please check it if want to know more.

FAQ: Q. I want to use OR and also do casting as (T&). A. since OR's return type is reference, you may simply write some code like below.

class A {};
class B : public A {};
A* foo() { return new B(); }
int main() {
B& value = (A&) foo() OR.ret(-1); // <-- but a compile error occurs
here. return 0;
}

the reason is that, as mentioned earlier, the left side of OR must always be a pointer. therefore, it should not be (T&) but (T*). this is correct codes.

int main() {
B& value = (B*) foo() OR.ret(-1); // or you may use `auto&`
return 0;
}
if you want to cast the final result of a TO() chain to T&, you must
surround the entire TO() chain with parentheses.
@code
B& value = (B*) (foo() TO(getA()) TO(getMayB())) OR.ret();
#define TO(fn)
Definition to.hpp:175

Q. Can I use OR after return? A. OR was created based on the precondition that it would be used when defining a variable. It cannot be used with the return keyword.

Q. I used OR macro with auto& and got Non-const lvalue reference to... error. my code is like below,

auto& ret = _sub[name].get() OR.ret();

A. don't use auto keyword. the actual return type of OR macro could be tmedium<T> or tstr or tweak. it differs in context which you're using. it was specified in each operand class file, for instance, 'tstr.hpp'. and if you used OR macro which returns tmedium, it is implicitly returned as T& through this class. therefore, unless you have a special situation where you want to use tmedium, specify the type directly instead of auto.

MyClass& ret = _sub[name].get() OR.ret();
// or,
tstr<MyClass> ret = youGetThisInstanceOnHeap() OR.ret();

Q. I used OR in a function whose return type is tmay and initialized it with a T&& variable, but the value is strange.

tmay<A> foo();
A&& a = foo() OR.ret();

A. TLDR; take rvalue with type tmay<A>&& just like rvalue reference to return type of the function.

tmay<A>&& a = foo();

foo() returns tmay by value. If you receive something returned by value as an rvalue reference, its life would be extended, but in this case, since it is not received as tmay, the value inside it is taken out and returned, so the tmay temporary object does not extend its life and starts to die immediately. as a result, a garbage value is bound to A&& a.

◆ __WHEN_OBJECT__

#define __WHEN_OBJECT__   __indep_when__

Early-return pattern macro for exception handling

The byeol project actively applies the early-return pattern throughout. This helps reduce code depth, improve code flow clarity, and handle exceptional situations immediately. However, traditional if statements make it difficult to distinguish between normal branching logic and early-return exception handling.

Problem

Consider this traditional early-return code:

str me::eval(const args& a) {
std::string key = _makeKey(a);
if(key.empty()) {
BY_E("key is empty");
return tstr<obj>();
}
if(_isSelfMaking(key)) {
BY_E("error: you tried to clone self generic object.");
return tstr<obj>();
}
if(!_cache.count(key))
_makeGeneric(key, params::make(_paramNames, a));
return _cache[key];
}
#define BY_E(fmt,...)
Log macro: prints debug log on console and file.
Definition macro.hpp:22

The WHEN macro solves this by explicitly marking early-return cases. It is used exclusively for early-return patterns. Additionally, since over 90% of early-returns involve logging an error and returning an error value, WHEN supports chaining to express both operations in a single line.

Solution

The same code becomes much clearer with WHEN:

str me::eval(const args& a) {
std::string key = _makeKey(a);
WHEN(key.empty()).err("key is empty").ret(tstr<obj>());
WHEN(_isSelfMaking(key))
.err("error: you tried to clone self generic object.")
.ret(tstr<obj>());
if(!_cache.count(key)) _makeGeneric(key, params::make(_paramNames,
a)); return _cache[key];
}

Now the purpose of each if is clear, and exception handling is visually distinct from normal branching logic.

Remarks
WHEN macro is used very frequently throughout the project, so it's important to understand it well.
WHEN_OBJECT customization Since byeol uses a multi-layered architecture, different layers may need different behavior when WHEN conditions are met. Low-level layers simply output logs to the screen, but high-level layers require more complex processing like creating exception objects with stacktrace information. This is solved by redefining __WHEN_OBJECT__ in each layer.

◆ _ON_EACH_DECL

#define _ON_EACH_DECL ( cmd)    __BY__DECL_##cmd

byeol universal macro

This reduces the possibility of macro conflicts between different libraries and makes macro writing easier. and this is generalized API used to describe the metadata of class in byeol. BY can be used to define detailed metadata about a class by chaining sub-command sets. these sub-commands are macros available only inside of BY.

for instance,

class Foo {
BY(CLASS(Foo), VISIT(Foo), ...)
public:
...and your codes...
};

Each command preceding BY is prefixed with the __BY__DECL_ prefix. The following commands can be used within the BY macro: CLASS: Injects metadata for a concrete class. VISIT: Makes the class visitor-friendly. ME: Adds typedefs named super and me. DEF_ME: Same as ME, but used in the implementation file. ADT: Injects metadata for an abstract class. CLONE: Adds a virtual copy constructor.

◆ BY_DEF_ME_2

#define BY_DEF_ME_2 ( ME,
SUPER )   __BY__DECL_DEF_ME_2(ME, SUPER)

byeolMeta macro's sub-commands, DEF_ME

Similar to ME, this macro adds typedefs called me and super to the scope of the current compilation unit.

This macro was added for use when writing implementation files. Because the ME sub-command adds typedefs to the class scope, it's not available for you to access them outside of member fuctions when writing implementation file.

let me give you example:

// Foo.cpp --
BY(DEF_ME(Foo))
// From now on, new typedef `me` refers To Foo type, and `super` refers
// to Foo::super.
// if there's no Foo::super, you'll have some compile errors when use
it.
me::Foo() {
// if you didn't use DEF_ME above, `me::Foo() {` wouldn't be
possible.
// Instead, you should use `Foo::Foo() {` 'cause by the time use
`me::`,
// it's not in a member funtion.
}

◆ BY_OVERLOAD

#define BY_OVERLOAD ( NAME,
... )   BY_CONCAT(NAME##_, __OVERLOAD_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

Macro Overloding:

by count of given arguments, let compiler determine which macro should works. original source code from BuvinJ at https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments

usage:

// define macro:
#define MyMacro_0() 100
#define MyMacro_1(x) (x)+5
#define MyMacro_2(x, y) (x)+(y)
#define MyMacro(...) BY_OVERLOAD(MyMacro, __VA_ARGS__)
// using:
cout << MyMacro(5) << ", " << MyMacro(3, 5) << ", " << MyMacro() <<
"\n";

expected output should be, 10, 8, 100.

◆ BY_PAIR_1

#define BY_PAIR_1 ( x)    x

Funnel

This macro mostly used to put in arguments to macro behind macro. Why does we need this?:

#define My_2(X, y) .......

But, if you put class template as macro argument, expanding macro gets messed up. My_2(template<int, float>, template<A, B>) // in fact, preprocessor thought we  passed 4 arguments, not 2.

Then, Use Funnel macro instead:

My_2(BY_PAIR(MyMap<T, U>), BY_PAIR(template <typename T, typename U))

◆ BY_SIDE_FUNC

#define BY_SIDE_FUNC ( ...)    BY_OVERLOAD(BY_SIDE_FUNC, __VA_ARGS__)

Side function macros for safe pointer operations

Side Func is a term I coined. It refers to an overloaded function added for the convenience of the original function. For example:

void foo(std::string name, std::string value);
// This is a side func.
void foo(std::string value) { return foo("default", value); }

This macro makes it easy to create side functions like the one above.

◆ TO

#define TO ( fn)
값:
->*[&](auto&& __p) -> decltype(__p.fn) { \
return __p.fn; \
}

@breif safe navigation feature of c++

TO supports the safe navigation features of modern languages very intuitively and naturally. the basic usage is <expression> TO(yourAccessor()). let's explain with an example first before explain further.

usage: let's assume that we have following classes.

struct Resource {
Pallete* getPallete(); // this can return nullptr.
};
struct Pallete {
Canvas* getCanvas(); // this can return nullptr.
};
struct Canvas {
Brush& getBrush(int type); // this can't return nullptr.
};

and what if there are funcs utilize above structs. in language spec of c++, there is no sort of safe navigation thing, so we've to do like this.

int getBrushColorCode(Resource r) {
auto* pallete = r.getPallete();
if(!pallete) {
log("pallete is null");
return -1;
}
auto* canvas = pallete->getCanvas();
if(!canvas) {
log("canvas is null");
return -1;
}
Brush& brush = canvas->getBrush(BrushType.SYSTEM);
return brush.getColorCode();
}

of course, this example illustrates a rather extreme train wreck pattern, and is a design that should be avoided, but situations where you need to access a pointer to a certain number of pointers occur frequently, and if you don't always check in advance whether the pointer is valid every time you dereference it, UB will occur.

 to be:
     with safe navigation, whether you will receive nullptr or not is

determined after the dereference chain of all pointers is finished. so the resulting code can become very concise.

int getBrushColorCode(Resource r) {
int* code = r TO(getPallete()) TO(getCanvas())
TO(getBrush(BrushType.SYSTEM)) TO(getColorCode()); if(!code)
{ log("code is null") return -1;
}
return *code;
}
Remarks
as you can see, it looks easy to use, but there are a few things that you should be aware of.
  1. the previously mentioned <expression> does not simply mean pointers and references, but also includes classes that satisfy the following conditions.

         a. a class that defines operator->().
         b. a class that defines operator*().
         c. a class that defines operator bool().
    
     you may have noticed that the classes that satisfy the above conditions
    

    are usually smart pointers like unique_ptr. the byeol repository provides separate smart pointers and classes that replace std::optional<T> for API consistency and safe type checking. e.g. tstr<T>, tweak<T>, binder, tmay<T>, tres<T> are included here.

     the TO() macro is designed to work properly even if the return value of
    

    the function is a value or reference to the above class. it works even if it exists in the middle of safe navigation chain. for example,

     @code
         struct Resource {
             Pallete* getPallete(); // this can return nullptr.
         };
         struct Pallete {
             Canvas& getCanvas(); // this *never* returns nullptr.
         };
         struct Canvas {
             tstr<Brush> getBrush(int type); // this is not pointer, but
             pointer-like-variable.
         };
    
         int getBrushColorCode(Resource r) {
             // however you can do exactly same like above example.
             int* code = r TO(getPallete()) TO(getCanvas())
             TO(getBrush(BrushType.SYSTEM)) TO(getColorCode())
    
             // but you may notice that you don't have to put `TO` for
    

    reference type. so, int* code = r TO(getPallete().getCanvas()) TO(getBrush(BrushType.SYSTEM)) TO(getColorCode());

    if(!code) return -1; return code; }

  2. if nullptr returned during the chain, the final result value becomes the nullptr of last type of the chain. if it is T*, it will be nullptr, but if it is T, i.e. a function that returns by value, the return value will be T{}.
     3. don't recommend you to put a reference in `TO()`
     references are always non-null, so you can access them directly.
    
     4. it is not recommended for any function to return a pointer type to a
     pointer-like-variable.
     pointer-like-variable is a sufficiently lightweight class. You can
    
    return it by value, or if you don't like that, return it by reference.
     5. it goes very well with OR macro.
     please check the usage of OR macro in advance. If you also use WHEN
    
    macro, the code will become way more concise.
     @code
         int getBrushColorMode(Resource r) {
             // this uses OR macro. so final type of the chain is `int&`.
             // but `int&` can be copied into new variable `int`.
             int code = r TO(getPallete())
    
    TO(getCanvas().getBrush(BrushType.SYSTEM)) TO(getColorCode()) OR.err("code is null").ret(-1) return code; }
to Top