The following:
import 'package:flutter/foundation.dart';
class Bar {
final int tender;
const Bar(this.tender);
}
class Foo {
final int foo;
final Bar bar;
const Foo({#required foo})
: foo = foo,
bar = const Bar(foo);
}
results in the compiler error "Arguments of a constant creation must be constant expressions" for the attempted initialization bar = const Bar(foo). Why does that line fail, when the preceding line foo = foo does not?
EDIT Just to further clarify the source of confusion: effectively, it's as if it's ok to const-construct a Foo with the argument foo (which is unknown at compile-time), but it's not ok to const-construct a Bar with the exact same argument. Why?
The line fails because a variable is being passed instead of a constant/literal to create the const object of Bar. The compiler doesn't know what foo is going to be at compile time and hence it fails to create a const Bar(foo)
Consider this :
void main(){
int someNonConstantValue = 10;
const Bar(someNonConstantValue);
}
Compiler warns here with the same error message
Arguments of a constant creation must be constant expressions
This is because someNonConstantValue is not a constant.
Now if the code is modified as :
void main(){
const int someConstantValue = 10;
const Bar(someConstantValue);
}
There are no warnings because compiler is now assured that someConstantValue is actually constant and wont change anytime (and hence it can compile and optimize the code).
Similarly, In the original example, compiler doesn't give error if changes are made as such to use a constant literal for creating const Bar(
class Bar {
final int tender;
const Bar(this.tender);
}
class Foo {
final int foo;
final Bar bar;
const Foo({#required this.foo})
: bar = const Bar(10);
}
Addition :
Following the same explanation as above, error is given if const Foo( is passed a variable
void main(){
int someNonConstantValue = 10;
const Foo(foo:someNonConstantValue);
}
The point you are referring to is the constructor declaration inside its own class. It hasn't been invoked with any value yet.
EDIT:
This github issue is a discussion on similar lines from early days of Dart.
This answer provides explanation about the same.
Related
The question came into my mind when I've declared a final callback in a class, and the constructor can be declared as const, and trying to make a constant value of a function like so:
class MyClass {
final void Function() callback;
const MyClass(this.callback);
}
void example1() {
const foo = MyClass(() {});
}
This gives the error:
Why can I delcare a constant constructor in the first place? What would make an object of MyClass compile-time constant if no function value can be constant?
A simpler example:
typedef MyVoidCallback = void Function();
void example2() {
const MyVoidCallback bar = () {};
}
This gives the error:
Thank you in advance
It is because () {} is not a constant value, it is rather creating a new instance every time. All functions in dart inherit Function class which doesn't have const constructor.
However, since functions are top-level members in dart, you can pass them by name (like a variable). So if you define your function outside of any class such that it is a global function, you can pass it as a parameter value in a const constructor.
UPDATE from #randal-schwartz comment: Static functions inside a class can also be passed into these const constructors as parameters.
Below code should work:
class MyClass {
final void Function() callback;
const MyClass(this.callback);
}
void example1() {
const foo = MyClass(doWork);
}
void doWork() {
// TODO: do work
}
I am passing a Function as an optional parameter to the constructor but I can't assign a default value.
void main() {
Person p = Person();
print(p.foo('Hello'));
}
class Person {
final String Function(String) foo;
Person({this.foo});
}
now trying to assign a default value: Person({this.foo = (val) {return val;});
produces the error: Error: Not a constant expression. I am aware the parameter must be const but using const or even static infront of (val) {return val;} does not work.
Does anyone have an idea how to solve this problem?
You can try this:
void main() {
Person pLower = Person(foo: (a) => a.toLowerCase());
print(pLower.foo('Hello'));
Person pDefault = Person();
print(pDefault.foo('Hello'));
}
class Person {
static String defaultFoo(String a) => a.toUpperCase();
final String Function(String) foo;
Person({this.foo = defaultFoo});
}
Output
hello
HELLO
You can only use constant values (aka. compile-time constants) as default values.
You cannot create a constant function literal, so there is no way to write the function in-line in the constructor.
However, references to top-level or static functions are constants, so you can declare the default value function as a static function or top-level function.
void main() {
Person p = Person();
print(p.foo('Hello')); // Prints "Hello"
}
class Person {
final String Function(String) foo;
Person({this.foo = _identity});
static String _identity(String value) => value;
}
// or as top-level.
// String _identity(String value) => value;
You can (and should) choose to make the function public if the default value is on an instance method, and you expect anyone to extend or implement your class. In that case, they need to declare the same default value.
Another option, which is often at least as useful, is to not use a default value, but replace a null before using the value:
class Person {
final String Function(String) foo;
Person({String Function(String) foo}) : foo = foo ?? _identity;
static String _identity(String value) => value;
}
or even using a non-constant value:
class Person {
final String Function(String) foo;
Person({String Function(String) foo}) : foo = (foo ?? (String x) => x);
}
For a constructor, it makes very little difference. If it was an instance method instead, using ?? to replace null avoids subclasses having to use the exact same function as default value.
Personally I recommend always using ?? instead of a default value. It's more flexible since it allows non-constant values. For non-function default values, you'll have to document the default behavior instead of just letting the dartDoc show {int x = 42}, but for functions, you'll have to document them anyway.
Does anyone know why the below code works in dart. final keyword is used to define constant variables. But the below code works little different. If we are using const with a different value it's working fine without giving error.
void main() {
ExampleFinal exampleFinal = new ExampleFinal();
}
class ExampleFinal() {
final a = 5;
ExampleFinal() {
// The below statement will not create any error.
// But if you are remove const in below line it'll show a compile time error.
const a = 6;
print(a); // Prints 6
}
}
Is it a bug or a feature in dart? There is nothing like mentioned in document also.
const a = 6;
creates a new variable that shadows the final a = 5;
This is possible because {...} creates a new scope in the constructor body.
If you add at the end of the constructor
print(this.a);
it will print 5
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Like the title suggests, what's the difference between static, final and const at compile time in Dart?
When are they computed and when are memory allocated for each type?
Can a heavy use of static variables lead to performance issues or OOM?
static is to declar class level members (methods, fields, getters/setters).
They are in the class' namespace. They can only be accessed from within the class (not subclasses) or with the class name as prefix.
class Foo {
static bar() => print('bar');
void baz() => bar(); // ok
}
class Qux extens Foo {
void quux() => bar(); // error. There is no `bar()` on the `Qux` instance
}
main() {
var foo = Foo();
foo.bar(); // error. There is no `bar` on the `Foo` instance.
Foo.bar(); // ok
}
const is for compile time constants. Dart allows a limited set of expressions to calculate compile time constants.
const instances are canonicalized. This means multiple const Text('foo') (with the same 'foo' parameter value) are canonicalized and only one single instance will be created no matter where and how often this code occurs in your app.
class Foo {
const Foo(this.value);
// if there is a const constructor then all fields need to be `final`
final String value;
}
void main() {
const bar1 = Foo('bar');
const bar2 = Foo('bar');
identical(bar1, bar2); // true
}
final just means it can only be assigned to at declaration time.
For instance fields this means in in field initializers, by constructor parameters that assign with this.foo, or in the constructor initializer list, but not anymore when the constructor body is executed.
void main() {
final foo = Foo('foo');
foo = Foo('bar'); // error: Final variables can only be initialized when they are introduced
}
class Foo {
final String bar = 'bar';
final String baz;
final String qux;
Foo(this.baz);
Foo.other(this.baz) : qux = 'qux' * 3;
Foo.invalid(String baz) {
// error: Final `baz` and `qux` are not initialized
this.baz = baz;
this.qux = 'qux' * 3;
}
}
A static variable does not require an instance of the class to use.
Example:
class Math {
static var double staticPi = 3.14;
double var pi = 3.14;
}
class ClassThatUsesMath {
print(Math.staticPi);
// Non-static variable must initialize class first:
Math math = Math();
print(math.pi);
}
A final variable's value cannot be changed after it is assigned a value.
Example:
class Math {
final var pi = 3.14;
pi = 3.1415; // Error!
}
A const variable is similar to a final one in that it is immutable (cannot be changed). However, const values must be able to be calculated at compile time. const values will also be reused instead of being recalculated.
Example:
class MathClass {
const var section = 4;
const var examTime = DateTime.now(); // Error! Cannot be determined at compile time.
}
See update 1 below for my guess as to why the error is happening
I'm trying to develop an application with some C#/WPF and C++. I am having a problem on the C++ side on a part of the code that involves optimizing an object using GNU Scientific Library (GSL) optimization functions. I will avoid including any of the C#/WPF/GSL code in order to keep this question more generic and because the problem is within my C++ code.
For the minimal, complete and verifiable example below, here is what I have. I have a class Foo. And a class Optimizer. An object of class Optimizer is a member of class Foo, so that objects of Foo can optimize themselves when it is required.
The way GSL optimization functions take in external parameters is through a void pointer. I first define a struct Params to hold all the required parameters. Then I define an object of Params and convert it into a void pointer. A copy of this data is made with memcpy_s and a member void pointer optimParamsPtr of Optimizer class points to it so it can access the parameters when the optimizer is called to run later in time. When optimParamsPtr is accessed by CostFn(), I get the following error.
Managed Debugging Assistant 'FatalExecutionEngineError' : 'The runtime
has encountered a fatal error. The address of the error was at
0x6f25e01e, on thread 0x431c. The error code is 0xc0000005. This error
may be a bug in the CLR or in the unsafe or non-verifiable portions of
user code. Common sources of this bug include user marshaling errors
for COM-interop or PInvoke, which may corrupt the stack.'
Just to ensure the validity of the void pointer I made, I call CostFn() at line 81 with the void * pointer passed as an argument to InitOptimizer() and everything works. But in line 85 when the same CostFn() is called with the optimParamsPtr pointing to data copied by memcpy_s, I get the error. So I am guessing something is going wrong with the memcpy_s step. Anyone have any ideas as to what?
#include "pch.h"
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace std;
// An optimizer for various kinds of objects
class Optimizer // GSL requires this to be an unmanaged class
{
public:
double InitOptimizer(int ptrID, void *optimParams, size_t optimParamsSize);
void FreeOptimizer();
void * optimParamsPtr;
private:
double cost = 0;
};
ref class Foo // A class whose objects can be optimized
{
private:
int a; // An internal variable that can be changed to optimize the object
Optimizer *fooOptimizer; // Optimizer for a Foo object
public:
Foo(int val) // Constructor
{
a = val;
fooOptimizer = new Optimizer;
}
~Foo()
{
if (fooOptimizer != NULL)
{
delete fooOptimizer;
}
}
void SetA(int val) // Mutator
{
a = val;
}
int GetA() // Accessor
{
return a;
}
double Optimize(int ptrID); // Optimize object
// ptrID is a variable just to change behavior of Optimize() and show what works and what doesn't
};
ref struct Params // Parameters required by the cost function
{
int cost_scaling;
Foo ^ FooObj;
};
double CostFn(void *params) // GSL requires cost function to be of this type and cannot be a member of a class
{
// Cast void * to Params type
GCHandle h = GCHandle::FromIntPtr(IntPtr(params));
Params ^ paramsArg = safe_cast<Params^>(h.Target);
h.Free(); // Deallocate
// Return the cost
int val = paramsArg->FooObj->GetA();
return (double)(paramsArg->cost_scaling * val);
}
double Optimizer::InitOptimizer(int ptrID, void *optimParamsArg, size_t optimParamsSizeArg)
{
optimParamsPtr = ::operator new(optimParamsSizeArg);
memcpy_s(optimParamsPtr, optimParamsSizeArg, optimParamsArg, optimParamsSizeArg);
double ret_val;
// Here is where the GSL stuff would be. But I replace that with a call to CostFn to show the error
if (ptrID == 1)
{
ret_val = CostFn(optimParamsArg); // Works
}
else
{
ret_val = CostFn(optimParamsPtr); // Doesn't work
}
return ret_val;
}
// Release memory used by unmanaged variables in Optimizer
void Optimizer::FreeOptimizer()
{
if (optimParamsPtr != NULL)
{
delete optimParamsPtr;
}
}
double Foo::Optimize(int ptrID)
{
// Create and initialize params object
Params^ paramsArg = gcnew Params;
paramsArg->cost_scaling = 11;
paramsArg->FooObj = this;
// Convert Params type object to void *
void * paramsArgVPtr = GCHandle::ToIntPtr(GCHandle::Alloc(paramsArg)).ToPointer();
size_t paramsArgSize = sizeof(paramsArg); // size of memory block in bytes pointed to by void pointer
double result = 0;
// Initialize optimizer
result = fooOptimizer->InitOptimizer(ptrID, paramsArgVPtr, paramsArgSize);
// Here is where the loop that does the optimization will be. Removed from this example for simplicity.
return result;
}
int main()
{
Foo Foo1(2);
std::cout << Foo1.Optimize(1) << endl; // Use orig void * arg in line 81 and it works
std::cout << Foo1.Optimize(2) << endl; // Use memcpy_s-ed new void * public member of Optimizer in line 85 and it doesn't work
}
Just to reiterate I need to copy the params to a member in the optimizer because the optimizer will run all through the lifetime of the Foo object. So it needs to exist as long as the Optimizer object exist and not just in the scope of Foo::Optimize()
/clr support need to be selected in project properties for the code to compile. Running on an x64 solution platform.
Update 1: While trying to debug this, I got suspicious of the way I get the size of paramsArg at line 109. Looks like I am getting the size of paramsArg as size of int cost_scaling plus size of the memory storing the address to FooObj instead of the size of memory storing FooObj itself. I realized this after stumbling across this answer to another post. I confirmed this by checking the value of paramsArg after adding some new dummy double members to Foo class. As expected the value of paramsArg doesn't change. I suppose this explains why I get the error. A solution would be to write code to correctly calculate the size of a Foo class object and set that to paramsArg instead of using sizeof. But that is turning out to be too complicated and probably another question in itself. For example, how to get size of a ref class object? Anyways hopefully someone will find this helpful.